本文作者:黄海燕,叩丁狼高级讲师。原创文章,转载请注明出处。
1. volatile 关键字
1.1 volatile 关键字作用:
在百度百科截取的描述如下:
image.png说明volatile 关键字作用作用有两点:
-
防止指令重排:规定了volatile 变量不能指令重排,必须先写再读。
-
内存可见:线程从内存中读取volatile修饰的变量的数据,直接从主内存中获取数据,不需要经过CPU缓存,这样使得多线程获取的数据都是一致的。如图所示:
image.png
1.2 volatile和synchronized的区别
volatile不能够替代synchronized,原因有两点:
1.对于多线程,不是一种互斥关系
2.不能保证变量状态的“原子性操作”,所以volatile不能保证原子性问题
1.3解决单例设计模式线程安全问题
实现单例设计模式两种
- 饿汉式(不存在原子性,是线程安全的)
实现1:
//饿汉式:很饿需要立马创建对象
public class Singleton1 {
//1.定义一个对象
private static final Singleton1 instance = new Singleton1();
//2.私有化构造器,避免外部类创建对象
private Singleton1(){}
//3.获取对象的静态方法
public static Singleton1 getInstance(){
return instance;
}
}
实现2:枚举方式(最安全)
//饿汉式(枚举)
public enum EnumSingleton {
INSTANCE;
}
- 懒汉式(懒加载):存在原子性问题,线程不安全
//懒汉式:很懒,使用对象的时候才创建对象,但是省资源
public class Singleton2 {
private static Singleton2 instance;
private Singleton2() {
}
public static Singleton2 getInstance() {
if (instance == null) {
instance = new Singleton2();
}
return instance;
}
}
①由于 instance = new Singleton2();存在原子性问题,所以我们应该用synchronized代码块将其同步。这里由于synchronized很耗资源,所以粒度越小越好,最好不要使用同步方法。
public static Singleton2 getInstance() {
if (instance == null) {
synchronized (Singleton2.class) {
instance = new Singleton2();
}
}
return instance;
}
②在多个线程的情况,可能存在线程1和线程2都已经执行了instance == null的判断,可能线程1抢到了锁线程2就阻塞在了同步代码块入口,当线程1执行完毕释放锁,线程2拿到锁的时候因为之前判断instance == null为true就会创建对象,那么此时就无法保证单例了,所以我们应该继续在同步代码块中再判断一次instance == null。这样的做法我们有个专业名词,称之为双重检查锁定。
public static Singleton2 getInstance() {
if (instance == null) {
synchronized (Singleton2.class){
if (instance == null) {
instance = new Singleton2();
}
}
}
return instance;
}
③instance = new Singleton2();这句代码存在指令重排问题,什么意思?
一般的执行顺序为:
1)给对象分配内存空间
2)初始化对象
3)变量instance 指向内存空间
在单线程中,由于步骤2)和步骤3)即使交换顺序也不会影响最终效果,所以可能发生指令重排,顺序为:
1)给对象分配内存空间
3)变量instance 指向内存空间
2)初始化对象
如果出现指令重排就会发生以下问题,如图所示:
image.png注意:由于线程2在外面的判断就为false,没有去运行需要竞争锁的代码,所以没有进入阻塞状态,和线程1是并行状态,导致访问对象出现问题,所以为了避免这个问题,我们应该不让指令重排发生,那么使用volatile修饰对象,让对象先写再读,固定对象的指令,避免指令重排。
最终线程安全的单例懒汉式代码如下:
public class Singleton2 {
private static volatile Singleton2 instance;
private Singleton2() {}
public static Singleton2 getInstance() {
if (instance == null) {
synchronized (Singleton2.class){
if (instance == null) {
instance = new Singleton2();
}
}
}
return instance;
}
}
想获取更多技术视频,请前往叩丁狼官网:http://www.wolfcode.cn/all_article.html
网友评论