美文网首页
单例模式

单例模式

作者: 会飞的蜗牛F | 来源:发表于2020-08-30 12:58 被阅读0次

    饿汉式,安全的

    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;
        }
    }
    

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


    image.png

    当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.haomeiwen.com/subject/zrxesktx.html