單例模式學習 1 餓漢式單例模式 還沒用就創建了對象,可能會浪費空間 2 懶漢式單例模式 無線程鎖 java package main.java.com.yuehun.singleton; / main.java.com.yuehun.singleton @author yuehun Created ...
單例模式學習
1 餓漢式單例模式
package main.java.com.yuehun.singleton;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 餓漢式單例模式
public class Hungry {
private Hungry(){}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance()
{
return HUNGRY;
}
}
- 還沒用就創建了對象,可能會浪費空間
2 懶漢式單例模式
無線程鎖
package main.java.com.yuehun.singleton;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 懶漢式單例模式
public class Lazy {
private Lazy(){
System.out.println(Thread.currentThread().getName() + "OK");
}
private static Lazy lazy = null;
public static Lazy getInstance()
{
if (lazy == null)
{
lazy = new Lazy();
}
return lazy;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
{
new Thread(Lazy::getInstance).start();
}
}
}
- 單線程下可以,但是多線程不安全
加線程鎖
package main.java.com.yuehun.singleton;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 懶漢式單例模式
public class Lazy {
private Lazy(){
System.out.println(Thread.currentThread().getName() + " OK");
}
private volatile static Lazy lazy = null;
// 雙重檢測鎖模式的懶漢式單例模式, DCL 懶漢式
public static Lazy getInstance()
{
if (lazy == null) {
synchronized (Lazy.class) {
if (lazy == null) {
lazy = new Lazy(); // 不是一個原子性操作
/**
* 1 分配記憶體空間
* 2 執行構造方法,初始化對象
* 3 把這個對象指向這個空間
*
* 可能會發生指令重排!!!
*/
}
}
}
return lazy;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
{
new Thread(Lazy::getInstance).start();
}
}
}
3 單例不是安全的
使用反射破壞單例模式
- Java 裡面有個東西叫
反射
- 比如:
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 餓漢式單例模式
public class Hungry {
private Hungry(){}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance()
{
return HUNGRY;
}
public static void main(String[] args) throws Exception {
Hungry instance = new Hungry();
Constructor<Hungry> declaredConstructor = Hungry.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Hungry instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
阻止反射破壞
阻止
反射
的破壞
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 餓漢式單例模式
public class Hungry {
private Hungry(){
synchronized (Hungry.class)
{
if (HUNGRY != null)
{
throw new RuntimeException("不要試圖使用反射破壞單例模式");
}
}
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance()
{
return HUNGRY;
}
public static void main(String[] args) throws Exception {
Hungry instance = new Hungry();
Constructor<Hungry> declaredConstructor = Hungry.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Hungry instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
還有一種反射破壞
- 如果兩個對象都是通過反射構造的,就不會觸發異常
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 懶漢式單例模式
public class Lazy {
// private static boolean flag = false;
private Lazy()
{
synchronized (Lazy.class)
{
// if (!flag)
// flag = true;
// else {
if (lazy != null) {
throw new RuntimeException("不要試圖使用反射破壞單例模式");
// }
}
}
}
private volatile static Lazy lazy = null;
// 雙重檢測鎖模式的懶漢式單例模式, DCL 懶漢式
public static Lazy getInstance()
{
if (lazy == null)
{
synchronized (Lazy.class)
{
if (lazy == null)
{
lazy = new Lazy(); // 不是一個原子性操作
/**
* 1 分配記憶體空間
* 2 執行構造方法,初始化對象
* 3 把這個對象指向這個空間
*
* 可能會發生指令重排!!!
*/
}
}
}
return lazy;
}
public static void main(String[] args) throws Exception
{
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Lazy instance = declaredConstructor.newInstance();
Lazy instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
又有一種解決方法
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 懶漢式單例模式
public class Lazy {
private static boolean flag = false;
private Lazy()
{
synchronized (Lazy.class)
{
if (!flag)
flag = true;
else
throw new RuntimeException("不要試圖使用反射破壞單例模式");
}
}
private volatile static Lazy lazy = null;
// 雙重檢測鎖模式的懶漢式單例模式, DCL 懶漢式
public static Lazy getInstance()
{
if (lazy == null)
{
synchronized (Lazy.class)
{
if (lazy == null)
{
lazy = new Lazy(); // 不是一個原子性操作
/**
* 1 分配記憶體空間
* 2 執行構造方法,初始化對象
* 3 把這個對象指向這個空間
*
* 可能會發生指令重排!!!
*/
}
}
}
return lazy;
}
public static void main(String[] args) throws Exception
{
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Lazy instance = declaredConstructor.newInstance();
Lazy instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
破壞者又來了
- 可以通過反射獲取到我們設置的這個標誌位,在以後每一次創建對象的時候修改它
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 懶漢式單例模式
public class Lazy {
private static boolean flag = false;
private Lazy()
{
synchronized (Lazy.class)
{
if (!flag)
flag = true;
else
throw new RuntimeException("不要試圖使用反射破壞單例模式");
}
}
private volatile static Lazy lazy = null;
// 雙重檢測鎖模式的懶漢式單例模式, DCL 懶漢式
public static Lazy getInstance()
{
if (lazy == null)
{
synchronized (Lazy.class)
{
if (lazy == null)
{
lazy = new Lazy(); // 不是一個原子性操作
/**
* 1 分配記憶體空間
* 2 執行構造方法,初始化對象
* 3 把這個對象指向這個空間
*
* 可能會發生指令重排!!!
*/
}
}
}
return lazy;
}
public static void main(String[] args) throws Exception
{
Field flag = Lazy.class.getDeclaredField("flag");
flag.setAccessible(true);
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Lazy instance = declaredConstructor.newInstance();
flag.set(instance, false);
Lazy instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
道高一尺,魔高一丈
4 枚舉解決
- enum 本身也是一個 Class 類
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance()
{
return INSTANCE;
}
}
class Test {
public static void main(String[] args) throws Exception {
EnumSingleton instance1 = EnumSingleton.INSTANCE;
Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
EnumSingleton instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
到此,阻止反射破壞就成功了!