单例模式及其并发问题

单例模式是设计模式中使用比较广泛的一种设计模式,这个模式的目的是在系统中只实现一个类的实例。

首先给出一个Singleton的简单实现:

public class Singleton {
    private static Singleton singleton = null;
    private Singleton() {  }
    public static Singleton getInstance() {
        if (singleton== null) {
            singleton= new Singleton();
        }
        return singleton;
    }
}

1.构造函数的私有,表明了该类无法被外部实例化。

2.如果要获取该类的实例,只能通过该类的静态方法getInstance方法。

目前这个程序有个问题就是如果该类未初始化的时候同时有多个线程访问getInstance方法,它们的判断都为null,这个时候就会创建多个给 类的实例,这是不允许的。这种情况下就应该实现线程互斥。即加上synchronized来实现线程的互斥。 将上述代码更改为

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

上述代码中将会同时只有一个类进行实例化,但是如果同时进入多个程序判断为null,之后就会排队进行,但是已经判断结束了,所以仍旧会创建多个实例,只是不同时而已。

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

向上述代码中进行两次的判断非空,上述代码看起来没有问题了,但是仍存在一个小问题。
主要在于singleton = new Singleton()这句,这句并非是一个原子操作,事实上在JVM中这句话大概做了下面三件事情。
1.给Singleton分配空间。
2.调用Singleton的构造函数来初始化成员变量,形成实例。
3.将Singleton对象指向分配的内存空间(执行完这句Singleton才是非null了)
但是在JVM的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是1-2-3也可能是
1-3-2.如果是后者,则3执行完毕、2未执行之前,线程被抢占了,这时已经是非null了(但是初始化并未完成),所以线程二会不进行初始化而直接返
回,这回就会产生错误。
对此我们需要把Singleton声明成volatile就可以了。

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

使用volatile关键字有两个作用:
1)这个变量不会在多个线程中存在副本,而是直接从内存读取。
2)这个关键字会禁止指令重排序的优化。

public class Singleton
{
    private volatile static Singleton singleton = new Singleton();
    private Singleton()  {    }
    public static Singleton getInstance()   {
        return singleton;
    }
}

像上述代码中,singleton在第一次加载内存的时候就已经初始化了,后面的过程中没有初始化过程,也就不会产生Singleton实例,因此保证了线程的安全。但是上述代码即使未调用getInstance方法 也会产生一个Singleton实例。

public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

上述代码中采用了jvm本身的机制来保证了线程的安全,SingletonHolder是私有的,除了getInstance方法不能调用,这保证了只有getInstance方法调用的时候才进行初始化。而且在读的时候不存在问题。

时间: 2024-10-15 00:52:12

单例模式及其并发问题的相关文章

设计之单例模式

1.设计模式之单例模式 """ 方式一:__new__ class Singleton: __instance = None def __new__(cls, *args, **kwags): if not cls.__instance: cls.__instance = objecet.__new__(cls) return cls.__instance obj1 = Singleton() obj2 = Singleton() 方式二:classmethod class

SQL Server的高级知识

DataSet的内容介绍,两种单例模式(并发量的考虑),SQL高级中的case语句.连接操作.子查询.派生表 -------------------------------- 1.Case的用法 使用方法一:(类似C#中的case的用法) ->语法: Case  表达式 when 值1 then 返回值 when 值2 then 返回值 - End ->案例:把用户表中数据,全部查询出来,要求把DelFlag=0的显示成未删除 select *,case DelFlag when 0 then

java 链接Oracle数据库的工具类

java连接Oracle数据库的方式 1 特点:oracle.jdbc.OracleDriver是注册oracle驱动类: jdbc:oracle:thin:@localhost:1521:xe:连接oracle的方式:网络协议+访问方式+IP+端口号+xe数据库: user:hr数据库用户名 Password:hr数据库的用户密码 缺点:statement方式连接数据库容易被黑客注入式攻击 所有不安全 现在企业中很少采用这种方式的了 连接数据库后一定的关闭连接,这个最容易忘记 调用close(

简历准备

简历答疑准备 简历答疑准备专业技能答疑:1.orm框架2.restful接口规范3.Django restframework框架4.django框架5.分布式系统:6.缓存系统7.Mysql查询优化1.从索引上优化2.sql语句上优化8.分库分表,读写分离9.redis10.mongdb11.高并发负载均衡的处理1 什么是负载均衡?2.处理负载均衡的四种方式1.HTTP重定向实现负载均衡过程描述调度策略优缺点分析2. DNS负载均衡DNS是什么具体做法调度策略优缺点分析动态DNS综上所述3. 反

深入理解 Java 枚举

目录   1. 简介  2. 枚举的本质  3. 枚举的方法  4. 枚举的特性  5. 枚举的应用  6. 枚举工具类  7. 小结  8. 参考资料 ?? 本文已归档到:「javacore」 ?? 本文中的示例代码已归档到:「javacore」 1. 简介 enum 的全称为 enumeration, 是 JDK5 中引入的特性. 在 Java 中,被 enum 关键字修饰的类型就是枚举类型.形式如下: enum ColorEn { RED, GREEN, BLUE } 枚举的好处:可以将常

模块、设计模型、前端面试题积累

re 的 match 和 search 区别? match()函数只检测字符串开头位置是否匹配,匹配成功才会返回结果,否则返回None import re s1 = 'abcabcabc' print(re.match('abc', s1)) print(re.match('abc', s1).group()) print(re.match('abc', s1).span()) search()函数会在整个字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用

单例模式之懒汉的并发问题

饿汉模式: class Single{ private staitc final Single s= new Single(); private Single(){} public static Single getSingle(){ return s; } } 懒汉模式: class Single{ private static Single s= null; private Single(){} public static Single getSingle(){ if(s == null){

高并发下线程安全的单例模式

复制来自 http://blog.csdn.net/cselmu9/article/details/51366946 在所有的设计模式中,单例模式是我们在项目开发中最为常见的设计模式之一,而单例模式有很多种实现方式,你是否都了解呢?高并发下如何保证单例模式的线程安全性呢?如何保证序列化后的单例对象在反序列化后任然是单例的呢?这些问题在看了本文之后都会一一的告诉你答案,赶快来阅读吧! 什么是单例模式? 在文章开始之前我们还是有必要介绍一下什么是单例模式.单例模式是为确保一个类只有一个实例,并为整个

【转】高并发情况下的单例模式

如果在高并发时候,使用这种单例模式 publci class Singleton{      private static Singleton instance = null;      private Singleton(){} public static Singleton getInstance(){             if(instance == null){                    instance = new Singleton();             }