所谓线程不安全实际上就是一段代码在同一时间被两个线程同时执行,导致运行结果与单个线程运行结果不相同

新建一个单例模式类和一个多线程测试类

public class TestSingleTon implements Runnable{

	public static void main(String[] args) {
TestSingleTon t1 = new TestSingleTon();
TestSingleTon t2 = new TestSingleTon(); Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
} @Override
public void run() {
System.out.println(SingleTon.getInstance()); } } public class SingleTon {
private static SingleTon singleTon; public static SingleTon getInstance() {
    if(singleTon==null) {
singleTon = new SingleTon();
}
return singleTon;
} }

执行后发现控制台打印了两个不同的对象:

com.wey.demo.SingleTon@22896964
com.wey.demo.SingleTon@1ac5e970

说明有线程并发访问安全问题,获取的不是同一个实例

解决方案(1):使用同步锁机制,最简单的是在getInstance()方法上加synchronized关键字

public synchronized static SingleTon getInstance() {
if(singleTon==null) {
singleTon = new SingleTon();
}
return singleTon;
}

对于这种方式,有人觉得在多并发的情况下,每次获取实例都要判断锁,效率比较低下,所以就有人想出了这样的办法,双重判断实例,这种大大减少判断同步锁的次数了。所以实际使用中可以推广。

public static SingleTon getInstance() {
if(singleTon==null) {
synchronized (SingleTon.class) {//SingleTon的字节码
if(singleTon==null) {
singleTon = new SingleTon();
}
}
}
return singleTon;
}

同时用volatile关键字修饰singleTon即:

private volatile static Singleton singleTon = null;

完整的代码应该是这样的:

public class SingleTon {
private static SingleTon singleTon; public static SingleTon getInstance() {
  if(singleTon==null) {
synchronized(SingleTon.class) {
if(singleTon==null) {
singleTon = new SingleTon();
}
}
}
return singleTon;
} }
为什么要使用volatile 修饰singleTon?

主要在于singleTon = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情:

1.给 singleTon 分配内存

2.调用 Singleton 的构造函数来初始化成员变量

3.将singleTon 对象指向分配的内存空间(执行完这步 singleTon  就为非 null 了)。

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 singleTon  已经是非 null 了(但却没有初始化),所以线程二会直接返回 singleTon ,然后使用,然后顺理成章地报错

解决方案(2):改懒汉式单例为饿汉式单例

public class SingleTon {
private static SingleTon singleTon = new SingleTon(); public static SingleTon getInstance() {
return singleTon;
} }

线程安全的"懒汉"单例模式的更多相关文章

  1. 饿汉单例模式 and 懒汉单例模式

    饿汉单例模式:主要就是利用static关键字,在类加载的时候生成实例,调用效率高 但是如果一直没有调用getInstance方法的话,就会造成资源浪费 具体实现如下: class Single{ pr ...

  2. C++实现线程安全的单例模式

    在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式.单例模式分为懒汉模式,跟饿汉模式两种. 首先给出饿汉模式的实现 template <class T> class sing ...

  3. java23种设计模式(三)单例模式

    原文地址:https://zhuanlan.zhihu.com/p/23713957 一.概述 1.什么是单例模式? 百度百科是这样定义的:单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个 ...

  4. C++ 常用设计模式(学习笔记)

    1.工厂模式:简单工厂模式.工厂方法模式.抽象工厂模式 1).简单工厂模式:主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类. typedef enum { T80 ...

  5. C++的单例模式与线程安全单例模式(懒汉/饿汉)

    1 教科书里的单例模式 我们都很清楚一个简单的单例模式该怎样去实现:构造函数声明为private或protect防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实例,实 ...

  6. 一天一个设计模式——(Singleton)单例模式(线程安全性)

    一.模式说明 有时候,我们希望在应用程序中,仅生成某个类的一个实例,这时候需要用到单例模式. 二.模式类图 三.模式中的角色 Singleton角色,该模式中仅有的一个角色,该角色有一个返回唯一实例的 ...

  7. C#工具:ASP.NET MVC单例模式(懒汉)实现文件上传

    1.SingletonConfigRead帮助类 using System; using System.Collections.Generic; using System.IO; using Syst ...

  8. 设计模式之单例模式(Singleton)

    设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...

  9. 单例模式——创建型模式01

    1. 名称     单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类.单例模式是一种对象创建型模式. 2. 问题    ...

随机推荐

  1. 5月21 汽车查询及批量删除----php方法

    ---恢复内容开始--- 这个与之前不同是在php中实现了页面的查询,引用AJAX实现批量删除及弹窗的显示 作业要求: 页面显示数据代码: <!DOCTYPE html PUBLIC " ...

  2. 4月18 数据库的CRUD操作

    php主要是实现B/S Brower Server;此外还有C/S:Client Server暂时不考虑: LAMP: Linux系统 A阿帕奇服务器 Mysql数据库 Php语言,而现在学的是在wi ...

  3. express文件上传中间件Multer详解

    express文件上传中间件Multer详解 转载自:https://www.cnblogs.com/chengdabelief/p/6580874.html   Express默认并不处理HTTP请 ...

  4. PAT 1003 Emergency

    1003 Emergency (25 分)   As an emergency rescue team leader of a city, you are given a special map of ...

  5. 【LeetCode】数独

    判断一个数独是否合法,未填的空格用字符 ' . ' 表示.该数独有解并不是必要的. e.g. 如图合法数独,输入 ["53..7....","6..195..." ...

  6. java.sql.SQLException: Parameter index out of range (1 > number of parameters, which is 0).

    java.sql.SQLException: Parameter index out of range (1 > number of parameters, which is 0). at co ...

  7. nc(netcat)扫描开放端口

    探测单个端口是否开放可以用telnet,专业探测端口可以用Nmap,而对于非渗透用途的Linux可以直接用netcat. 1.使用netcat探测端口是否开放 nc -z -v - #z代表不交互要不 ...

  8. burpsuite拦截https数据包(Firefox)

    1.配置浏览器对http/https都使用burpsuite代理 http和https是分开的,对http使用了代理并不代表对https也使用了代理,要配置浏览器让其对https也使用同样的代理. 当 ...

  9. Oracle的创建表和创建约束的Sql语句

    Oracle的创建表和创建约束的Sql语法 1.创建表的语句 ---1.创建模拟的数据表 --- --1.1.创建学生表Student create table Student( StuId NUMB ...

  10. win7 php nginx 启动命令

    1 php 启动命令 @echo off e: cd E:/php-/ echo "php is starting on port 9007, php_version is 7.0.6&qu ...