java设计模式--单例模式---线程安全的懒汉式

原创 2018年04月15日 20:06:14

设计模式是一套被反复使用、多数人知晓的经过分类编目的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解,保证代码可靠性。

在开发过程中,有些对象,我们只需要一个,比如:配置文件、工具类、线程池、缓存、日志对象等。这些对象如果被创造出了多个实例,就会导致许多问题,例如占用过多资源,读写文件不一致等。
怎么保证整个应用中某个实例有且只有一个?—-单例模式
单例模式的饿汉式实现

public class Singleton {
    //1.构造方法私有化,不允许外部直接创建对象
    private Singleton(){}
    //2.创建类的唯一实例---该类只要一加载就会创造一个唯一实例---饿汉式实现
    private static Singleton instance = new Singleton();
    //3.提供一个用于获取实例的方法
    public static Singleton getInstance() {
        return instance;
    }
}

饿汉式在类加载的过程中就会创建一个本类的静态对象,所以它的加载过程比懒汉式慢,但是获得类实例的过程比懒汉式快,并且它在多线程模式下比较安全。缺点在于没有调用方法前就加载了,比较占用内存。
单例模式的懒汉式实现

public class SingletonII {
    private SingletonII() {
    }

    private static SingletonII instance;

    public static SingletonII getInstance() {
        if (instance == null) {//1:读取instance的值
            instance = new SingletonII();//2: 实例化instance
        }
        return instance;
    }
}
测试类:
/**
 * Created by dell on 2018/4/15.
 * 饿汉模式加载类时比较慢,运行时,获取对象的速度比较快。因为加载类时已经创建了类的唯一实例,线程安全。
 * 懒汉式加载类时比较快,运行时,获取对象的速度比较慢,而且有可能线程不安全。
 */
public class SingleTest {

    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        if (s1 == s2) {
            System.out.println("s1和s2指向同一个实例");
        } else {
            System.out.println("s1和s2指向不同的实例");
        }
        SingletonII s3 = SingletonII.getInstance();
        SingletonII s4 = SingletonII.getInstance();
        if (s3 == s4) {
            System.out.println("s3和s4指向同一个实例");
        } else {
            System.out.println("s3和s4指向不同的实例");
        }

        //检查懒汉式线程安全问题
        //newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
        ExecutorService threadPool = Executors.newFixedThreadPool(1000);
        for (int i = 0; i < 1000; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+":"+SingletonII.getInstance());
                }
            });
        }
    }
}

对于SingletonII代码注释部分,如果此时有两个线程,线程A执行到1处,读取了instance为null,然后cpu就被线程B抢去了,此时,线程A还没有对instance进行实例化。因此,线程B读取instance时仍然为null,于是,它对instance进行实例化了。然后,cpu就被线程A抢去了。此时,线程A由于已经读取了instance的值并且认为它为null,所以,再次对instance进行实例化。所以,线程A和线程B返回的不是同一个实例。
引发了单例模式的线程不安全问题。
解决方式:用synchronized修饰获取实例的方法

/**
 * Created by dell on 2018/4/15.
 * 单例模式的懒汉式实现
 */
public class SingletonII {
    private SingletonII() {
    }

    private static volatile SingletonII instance;
    //双重检查加锁机制
    public static synchronized SingletonII getInstance() {
        if (instance == null) {
            synchronized (SingletonII.class) {
                if (instance == null) {
                    instance = new SingletonII();
                }
            }
        }
        return instance;
    }
}

双重加锁机制配合volatile关键字。
instance = new SingletonII()其实可以分为下面的步骤:
1.申请一块内存空间;
2.在这块空间里实例化对象;
3.instance的引用指向这块空间地址;
指令重排序存在的问题是:
对于以上步骤,指令重排序很有可能不是按上面123步骤依次执行的。比如,先执行1申请一块内存空间,然后执行3步骤,instance的引用去指向刚刚申请的内存空间地址,那么,当它再去执行2步骤,判断instance时,由于instance已经指向了某一地址,它就不会再为null了,因此,也就不会实例化对象了。这就是所谓的指令重排序安全问题。那么,如何解决这个问题呢?加上volatile关键字,因为volatile可以禁止指令重排序。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ice_eyes/article/details/79950178

Dll中导出类--Delphi实战之一

   Dll中导出类--Delphi实战之一作者: Musicwind®创建时间: 2001-11-01~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~...
  • Musicwind
  • Musicwind
  • 2001-11-02 21:24:00
  • 1645

Java设计模式之单例模式(恶汉式和懒汉式)

/*  * 单例模式:  *         饿汉式:类一加载就创建对象  *         懒汉式:用的时候,才去创建对象  * 面试题:单例模式的思想是什么?写一个代码体现(我们最好写懒...
  • u012110719
  • u012110719
  • 2015-05-02 08:47:38
  • 4023

简单java单例模式 懒汉式、饿汉式以及解决线程安全问题(synchronized)

单例模式(又称设计模式) 一个类只存一个对象(实例)。 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界...
  • qq_37768482
  • qq_37768482
  • 2017-08-24 19:07:28
  • 587

懒汉式单例模式的线程安全问题

新建一个单例模式类和一个多线程测试类 public class Test24 implements Runnable { public void run() { System.out.print...
  • u010584063
  • u010584063
  • 2015-07-11 09:24:58
  • 3730

设计模式-单例模式(饿汉式及懒汉式的Java实现)

单例模式单例模式在程序设计中使用的频率非常之高,其设计的目的是为了在程序中提供唯一一个对象(保证只被构造一次),例如写入日志的log对象,windows的任务管理器实现(只能打开一个)。这里主要介绍单...
  • zjx409
  • zjx409
  • 2015-08-03 22:57:41
  • 5683

单例设计模模式——懒汉式为什么线程不安全

单例设计模式分为两种饿汉式,懒汉式无论哪一种,思想就三步,0;就一个实例,则不能被实例化,所有构造函数私有的1:提供一个实例2:提供一个供外部访问的方法 懒汉式线程不安全,那为什么不安全呢?看代码!...
  • L_ILove
  • L_ILove
  • 2017-01-09 21:39:00
  • 2949

单例模式--饿汉式、懒汉式、线程安全的懒汉式、高性能安全的懒汉式

单例模式学习小结 饿汉式单例:开发时较为常用。 class Single1 { private Single1(){} private static Single1 single = new Sin...
  • wanghantong
  • wanghantong
  • 2011-08-22 20:52:43
  • 4478

java 单例模式之线程安全的饿汉模式和懒汉模式

单例模式 解决的问题:保证一个类在内存中的对象唯一性. 比如:多程序读取一个配置文件时,建议配置文件封装成对象。会方便操作其中数据,又要保证多个程序读到的是同一个配置文件对象,就需要该配置文件对象在...
  • twj13162380953
  • twj13162380953
  • 2016-12-25 15:26:26
  • 2053

单例设计模式详解一:不安全的懒汉式(多线程环境验证)

单例设计模式详解一:不安全的懒汉式(多线程环境验证)单例设计模式详解一不安全的懒汉式多线程环境验证 写在前面的话 饿汉式 懒汉式 验证在多线程环境下懒汉式单例写法的不安全之处 写在前面的话前言:虽然工...
  • lw_power
  • lw_power
  • 2016-11-23 15:52:17
  • 1719

【单例模式】——饿汉模式与懒汉模式及其区别

单例模式是我们在开发过程中使用最多的一种设计模式。单例模式定义与作用就是保证整个程序中某个实例有且只有一个。我们都知道创造实例是通过构造方法来实现的,只要有构造方法,就可以创建实例。 一、单例模式构...
  • wpda_csdn
  • wpda_csdn
  • 2015-05-03 17:47:16
  • 5714
收藏助手
不良信息举报
您举报文章:java设计模式--单例模式---线程安全的懒汉式
举报原因:
原因补充:

(最多只允许输入30个字)