设计一个安全的单例模式

https://blog.csdn.net/Evan_QB/article/details/83537766

https://blog.csdn.net/liuchaoxuan/article/details/79840013 参考

https://blog.csdn.net/weixin_42613513/article/details/84844317 线程命名测试运行

饿汉式,安全的

public class SingletonExample2 {
    //私有化构造函数
    private SingletonExample2(){

    }
    //单例对象
    private static SingletonExample2 instance = new SingletonExample2();

    //静态工厂方法
    public static SingletonExample2 getInstance(){
       return instance;
    }
}

 

懒汉式 不安全的

public class SingletonExample1 {
    //私有化构造函数
    private SingletonExample1(){

    }
    //单例对象
    private static SingletonExample1 instance = null;

    //静态工厂方法
    public static SingletonExample1 getInstance(){
        if (instance == null){
            instance = new SingletonExample1();
        }
        return instance;
    }

 从写法上我们可以看出,饿汉模式是线程安全的,但它的性能上会大大折扣。那么我们能否也让懒汉模式也变得线程安全呢?答案是可以的 

方法一.在方法上加上同步锁

直接在获取实例的方法上加上synchronized关键字

public class SingletonExample3 {
    //私有化构造函数
    private SingletonExample3(){

    }
    //单例对象
    private static SingletonExample3 instance = null;

    //静态工厂方法
    public synchronized static SingletonExample3 getInstance(){
        if (instance == null){
            instance = new SingletonExample3();
        }
        return instance;
    }
}

  

虽说该方法是线程安全的,但其性能也和饿汉模式差不多,在性能上会大大折扣,别急我们接着看

方法二.使用双重校验加同步锁机制

public class SingletonExample4 {
    //私有化构造函数
    private SingletonExample4(){

    }
    //指令重排问题:
    //1.分配内存空间
    //2.初始化对象
    //3.instance = memory设置instance指向刚分配的内存

    //单例对象
    private static SingletonExample4 instance = null;

    //静态工厂方法
    public static SingletonExample4 getInstance(){
        if (instance == null){  //双重检测机制
            synchronized (SingletonExample4.class) {    //同步锁
                if (instance == null){
                    instance = new SingletonExample4();
                }
            }
        }
        return instance;
    }
}

  通过两次判断,确保创建的对象只能有一个,但这种方法还是存在线程安全的问题的。
在单线程的情况下,以上的代码没有丝毫问题,但在多线程的情况下,就会存在指令重排问题

 当A、B线程达到以上位置时,发生指令重排,在A线程执行到指令2(将对象的引用指向新分配的空间)时,刚好CPU被B占用,这样B的对象指向了一个内存空间,但其对象并没有被实例化

我们可以给代码加上一个volatile关键字来防止指令重排

//单例对象
private static volatile SingletonExample4 instance = null;

  

方法三.使用枚举模式来创建对象

有了以上方法为何还会需要第三种方法呢?那是因为java中还有一种暴力的创建方法,反射,虽然不能通过关键字new来创建对象,但通过反射创建的对象,就不会是单例的了,那么有什么办法可以解决吗?答案是有的,就是使用枚举。

public class SingletonExample7 {
    private SingletonExample7(){}

    public static SingletonExample7 getInstance(){
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton{
        INSTANCE;

        private SingletonExample7 singleton;

        //JVM保证这个方法绝对只调用一次
        Singleton(){
            singleton = new SingletonExample7();
        }

        public SingletonExample7 getInstance() {
            return singleton;
        }
    }
}

  

4、使用静态内置类实现单例模式

DCL解决了多线程并发下的线程安全问题,其实使用其他方式也可以达到同样的效果,代码实现如下:

package org.mlinge.s06;  
  
public class MySingleton {  
      
    //内部类  
    private static class MySingletonHandler{  
        private static MySingleton instance = new MySingleton();  
    }   
      
    private MySingleton(){}  
       
    public static MySingleton getInstance() {   
        return MySingletonHandler.instance;  
    }  
}  

  

 测试1:继承线程

public class Singleton {
    //防止指令重排
    private static volatile Singleton singleton =null;
    private Singleton(){
    }
    public static Singleton getSingleton() {
            if (singleton == null) {//双重检测
                synchronized (Singleton.class) {
                    if (singleton==null) {
                        singleton = new Singleton();
                    }
                }
            }

        return singleton;
    }
}

public class SingletonTest extends  Thread{

    public static void main(String ss[]){
        Thread[] tms = new Thread[10];
        for (int i = 0;i<tms.length;i++){
            SingletonTest singletonTest = new SingletonTest();
            singletonTest.setName("线程"+i);
            tms[i] = singletonTest;
        }
        for (int j = 0;j<tms.length;j++){
            tms[j].start();
        }
    }

    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"_"+Singleton.getSingleton().hashCode());
    }

  

测试2:实现runable

public class Singleton {
    //防止指令重排
    private static volatile Singleton singleton =null;
    private Singleton(){
    }
    public static Singleton getSingleton() {
            if (singleton == null) {//双重检测
                synchronized (Singleton.class) {
                    if (singleton==null) {
                        singleton = new Singleton();
                    }
                }
            }

        return singleton;
    }
}

public class SingletonRunableTest implements Runnable {

    public static void main(String ss[]){
        Thread[] tms = new Thread[10];
        for (int i = 0;i<tms.length;i++){
            SingletonRunableTest test = new SingletonRunableTest();
            tms[i] = new Thread(test,"线程"+i);
        }
        for (int j = 0;j<tms.length;j++){
            tms[j].start();
        }
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"_"+Singleton.getSingleton().hashCode());
    }

}

  

原文地址:https://www.cnblogs.com/Andrew520/p/11484809.html