当前位置 : 主页 > 网络编程 > PHP >

单例模式中懒汉模式的非线程安全问题的解决方法

来源:互联网 收集:自由互联 发布时间:2023-09-07
单例模式中懒汉模式的非线程安全问题的解决方法 package singleton1; //饿汉模式(立即加载) public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton


单例模式中懒汉模式的非线程安全问题的解决方法


package singleton1;

//饿汉模式(立即加载)
public class Singleton {

private static Singleton instance = new Singleton();

private Singleton(){}

public static Singleton getInstance(){
return instance;
}
}




而懒汉模式也叫延迟加载,即在get时才会被创建实例。懒汉模式在多线程的环境中就会出现多实例的情况,与单例模式相背离。如下代码可证:


package singleton2;
//懒汉模式
public class Singleton {

private static Singleton instance = null;

private Singleton(){}

public static Singleton getInstance(){
try {
if(instance==null){
Thread.sleep(1000);//模拟延迟加载
instance=new Singleton();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return instance;
}
}

创建线程:

package singleton2;

public class ThreadA extends Thread{

@Override
public void run(){
System.out.println(Singleton.getInstance().hashCode());//根据实例对象哈希值是否相同来判断对象是否相同
}

}

创建运行类:

package singleton2;

public class Run {

public static void main(String[] args) {
ThreadA t1=new ThreadA();
ThreadA t2=new ThreadA();
ThreadA t3=new ThreadA();
t1.start();
t2.start();
t3.start();
}

}

运行结果:

1321522194
2134502363
866891806

哈希值不同,所以对象不同,即不是单例模式。如何解决这个问题呢,下面提供了几种方法:





一、方法上声明synchronized关键字(效率低)


package singleton2;
//懒汉模式
public class Singleton {

private static Singleton instance = null;

private Singleton(){}
//1 方法上声明synchronized关键字
synchronized public static Singleton getInstance(){
try {
if(instance==null){
Thread.sleep(1000);//模拟延迟加载
instance=new Singleton();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return instance;
}
}



运行结果如下:


1876189785
1876189785
1876189785



二、同步代码块(效率低)


package singleton3;
//懒汉模式
public class Singleton {

private static Singleton instance = null;

private Singleton(){}

public static Singleton getInstance(){
try {
// 2 使用同步代码块,但效率低
synchronized(Singleton.class){
if(instance==null){
Thread.sleep(1000);//模拟延迟加载
instance=new Singleton();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return instance;
}
}



运行结果:


1619327594
1619327594
1619327594

       使用同步代码块方式也能解决上述问题,虽然不需要判断实例是否为null,但同样的也会带来效率低的毛病。



三、使用DCL(Double-Check Locking)双检查锁机制---(推荐)


public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}

public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}


这里为什么要用volatile修饰instance?

原因:在于instance = new Singleton()的时候,在内存中实际上是分3步执行的:


1)分配对象的内存空间:memory = allocate();


2)初始化对象:ctorInstance(memory);


3)指向分配的地址:instance =memory


多线程在执行的时候,2 3可能发生重排序。即有可能线程A执行到第3步的时候,读取到instance不为null,就返回。实际上此时还未执行第二部即未初始化。


加上volatile就可以避免2 3步重排序来保证线程安全。



四、使用静态内部类来实现单例模式


package singleton6staticinnerclass;

public class Singleton {

//静态内部类进行初始化
private static class SingletonHandler{
private static Singleton instance = new Singleton();
}

private Singleton(){}

public static Singleton getInstance(){
return SingletonHandler.instance;
}
}


五、static代码块来实现单例模式


package singleton7static;

public class Singleton {

private static Singleton instance = null;

private Singleton(){}
//静态代码块
static{
instance=new Singleton();
}
public static Singleton getInstance(){
return instance;
}
}


还有序列化和反序列化的单例模式来实现,或者使用枚举类来实现,在此就不一一介绍了,有兴趣可以百度下。


上一篇:dp中的滚动数组
下一篇:没有了
网友评论