美文网首页
单例模式

单例模式

作者: 会飞的蜗牛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());
    }
}

相关文章

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • 设计模式之单例模式详解

    设计模式之单例模式详解 单例模式写法大全,也许有你不知道的写法 导航 引言 什么是单例? 单例模式作用 单例模式的...

  • Telegram开源项目之单例模式

    NotificationCenter的单例模式 NotificationCenter的单例模式分析 这种单例模式是...

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • IOS单例模式的底层原理

    单例介绍 本文源码下载地址 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一...

  • 单例

    iOS单例模式iOS之单例模式初探iOS单例详解

  • 单例模式

    单例模式1 单例模式2

  • java的单例模式

    饿汉单例模式 懒汉单例模式

网友评论

      本文标题:单例模式

      本文链接:https://www.haomeiwen.com/subject/zrxesktx.html