饿汉式单例:
//饿汉式单例(造成内存资源浪费)
public class HungrySingle {
private HungrySingle(){
}
private final static HungrySingle HUNGRY_SINGLE = new HungrySingle();
public static HungrySingle getInstance(){
return HUNGRY_SINGLE;
}
}
懒汉式单例:
//懒汉式单例(用的时候才去实例化)
public class LazySingle {
private LazySingle(){
}
private static LazySingle lazySingle;
public static LazySingle getInstance(){
if (lazySingle==null){
lazySingle = new LazySingle();
}
return lazySingle;
}
}
问题:
懒汉式在多线程下:
public class LazySingle {
private LazySingle(){
System.out.println(Thread.currentThread().getName()+" OK");
}
private static LazySingle lazySingle;
public static LazySingle getInstance(){
if (lazySingle==null){
lazySingle = new LazySingle();
}
return lazySingle;
}
//多线程并发
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
LazySingle.getInstance();
}).start();
}
}
}
结果:(可能会出现多次创建对象——即和单例相违背)
Thread-0 OK
Thread-1 OK
双重检测锁模式的懒汉式单例(DCL懒汉式):
public class LazySingle {
private LazySingle(){
System.out.println(Thread.currentThread().getName()+" OK");
}
private static LazySingle lazySingle;
//双重检测锁模式的懒汉式单例,简称DCL懒汉式
public static LazySingle getInstance(){
if (lazySingle==null){
synchronized (LazySingle.class){
if (lazySingle==null){
lazySingle = new LazySingle();
}
}
}
return lazySingle;
}
//多线程并发
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
LazySingle.getInstance();
}).start();
}
}
}
结果:(可以发现在这种情况下单例能够保证)
Thread-0 OK
但是,上面的双重检测锁也可能出现问题:
因为new LazySingle();不是一个原子操作
new操作会经过三步:
1、分配内存空间
2、执行构造方法,初始化对象
3、把这个对象指向这个空间
以上三步期望的执行是1、2、3。但是如果发生指令重排,可能编程1、3、2,这就会导致发生错误。
于是在LazySingle前加上volatile
public class LazySingle {
private LazySingle(){
System.out.println(Thread.currentThread().getName()+" OK");
}
private volatile static LazySingle lazySingle;
//双重检测锁模式的懒汉式单例,简称DCL懒汉式
public static LazySingle getInstance(){
if (lazySingle==null){
synchronized (LazySingle.class){
if (lazySingle==null){
lazySingle = new LazySingle();
}
}
}
return lazySingle;
}
//多线程并发
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
LazySingle.getInstance();
}).start();
}
}
}
静态内部类实现的单例
//静态内部类实现的单例
public class InnerSingle {
private InnerSingle(){
}
public static InnerSingle getInstance(){
return InnerClass.INNER_SINGLE;
}
public static class InnerClass{
private static final InnerSingle INNER_SINGLE = new InnerSingle();
}
}
但是通过反射技术可以破解以上三种代码
利用反射破坏双重检测锁模式的懒汉式单例
public class TestReflect {
public static void main(String[] args) throws Exception {
LazySingle instance1 = LazySingle.getInstance();
//通过反射创建对象
Constructor declaredConstructor = LazySingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//暴力反射,强制访问私有构造方法
LazySingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
结果:(可以发现,结果的两个对象不是同一个对象,说明反射对单例模式进行了破坏)
main OK
main OK
com.company.single.LazySingle@1b6d3586
com.company.single.LazySingle@4554617c
如何避免通过反射进行破坏:
通过三重检测(即通过标志位在私有构造方法中加上标志判断)
public class LazySingle {
//利用标志位来进行判断,需要对关键字flag进行加密处理
private static boolean flag = false;
private LazySingle(){
synchronized (LazySingle.class){
if (flag==false){
flag = true;
}else{
throw new RuntimeException("不要试图通过反射破坏单例的异常");
}
}
}
private volatile static LazySingle lazySingle;
//双重检测锁模式的懒汉式单例,简称DCL懒汉式
public static LazySingle getInstance(){
if (lazySingle==null){
synchronized (LazySingle.class){
if (lazySingle==null){
lazySingle = new LazySingle();
}
}
}
return lazySingle;
}
}
结果:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.company.single.TestReflect.main(TestReflect.java:14)
Caused by: java.lang.RuntimeException: 不要试图通过反射破坏单例的异常
at com.company.single.LazySingle.(LazySingle.java:13)
... 5 more
但是还是可以用反编译获取flag变量名来进行破坏:
public static void main(String[] args) throws Exception {
//
Field flag = LazySingle.class.getDeclaredField("flag");
flag.setAccessible(true);
//通过反射创建对象
Constructor declaredConstructor = LazySingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//暴力反射,强制访问私有构造方法
LazySingle instance2 = declaredConstructor.newInstance();
flag.set(instance2,false);
LazySingle instance3 = declaredConstructor.newInstance();
System.out.println(instance2);
System.out.println(instance3);
}
结果:(可以发现反射又破坏了单例)
com.company.single.LazySingle@74a14482
com.company.single.LazySingle@1540e19d