并发编程之JMM&synchronized&volatile

-------------------------------------------------------------主要内容--------------------------------------------------------

1.JMM内存模型

2.主内存与工作内存之间数据同步的8大原子操作

3.并发的三个问题

        · 原子性

        · 可见性

        · 有序性

4.volatile内存语义 & 内存屏障

---------------------------------------------------------------------------------------------------------------------------------

1.JMM内存模型

JMM(JAVA memory model)即java内存模型,是一种抽象的概念,并不实际存在。JMM定义了数据的访问方式,包括线程私有的工作内存和线程间共享的共享内存。

线程的工作内存:

(1)线程独享,不存在线程安全性问题。

(2)拷贝的是主内存的副本。若主内存是基本数据类型,直接将值拷贝到线程的栈空间;若是实例对象,那么存储的是对象的引用。

(3)处理完成之后,会将工作内存的值刷回到主内存。

共享内存:

存储线程间共享的变量,可被多个线程访问,存在线程安全性问题。

※注意:共享内存并不单单指JVM堆内存,它是一个虚拟的概念,在硬件层面可包含寄存器、缓存和主内存。不要和JVM的线程模型混淆。

2.主内存与工作内存之间数据同步的8大原子操作

(1)Lock(锁定):将主内存中待访问的共享变量加锁。

(2)read(读取):将共享变量的副本拷贝出来,以供后续的load操作。

(3)load(加载):将读取的功能共享变量副本加载到工作内存。

(4)use(使用):将工作内存的变量传递给JVM执行引擎。

(5)assign(赋值):执行引擎将处理完的变量赋值给工作内存的变量。

(6)store(存储):将线程工作内部的变量传递出来,以便后续的write操作。

(7)write(写入):将工作内存的变量副本写入到共享内存。

(8)unlock(解锁):解除主内存中共享变量的锁定。

数据同步规则:

(1)变量只能在主内中诞生

(2)不允许一个线程无原因地(没有任何assign操作)刷新主内存中的变量

(3)一个变量同一时刻只能被一个线程锁定。可多次加锁,加锁和解锁成对出现。

(4)对变量lock,会清空工作内存中这个变量的值

(5)对变量unlock之前必须把值wrtie回主内存

3.并发的三个问题

原子性

原子性指的是一个操作是原子的,一旦开始就不会被别的线程影响。

(1)在JVM中,对基本数据类型的读写是原子的。

(2)还可以用synchronized和Lock保证操作的原子性。

可见性

由于线程会拷贝主内存的变量到工作内存,线程的工作内存又是独享的,对其他线程不可见。所以线程对变量的修改,是无法及时被其他线程感知到的。

在java中,可以使用volatile关键字来修饰成员变量。被volatile关键字修饰的成员变量,写是先于读的——一个线程修改了这个变量,会立刻将值刷回主内存,其他读线程会强制将工作内存中的变量副本失效,重新读取主内存中变量的值。这就保证了变量的修改能及时被其他线程感知到。在字节码层面,被volatile修饰的变量,会多出一个ACC_VOLATILE的特殊修饰。

另外,synchronized和lock保证了同一时刻变量只能被一个线程访问,并且在释放锁之前将变量的值刷新回主内存,也能保证可见性。

有序性

为了保证cpu的高性能,jvm会将操作指令进行重排。但是指令重排在多线程可能出现一些难以预料的问题。比如经典的单例模式的懒汉式实例化:

因为new DoubleCheckLock()可以分为以下3步

(1)memory=allocate();        // 分配内存空间

(2)instance(memory);        // 初始化对象

(3)instance=memory;         // instance指向刚分配的内存地址

由于指令重排只保证单线程语义完整性,那么(2)和(3)之间可能发生指令重排,就变成下面这样:

(1)memory=allocate();        // 分配内存空间

(2)instance=memory;         // instance指向刚分配的内存地址

(3)instance(memory);        // 初始化对象

线程A如果执行完(2)之后,失去了CPU的使用权,线程B此时来使用这个未初始化完成的变量,就会出现问题。

4.volatile内存语义 & 内存屏障

volatile提供了一种轻量级的锁机制。volatile主要包含下面两重语义:

(1)保证修饰的变量(只能修饰成员变量),线程A对其修改能及时被其他线程感知

(2)禁止指令重排

volatile使用了内存屏障来禁止指令重排。

硬件级的内存屏障包括:

lfence,是一种Load Barrier 读屏障

sfence, 是一种Store Barrier 写屏障
mfence, 是一种全能型的屏障,具备ifence和sfence的能力

JVM则封装了内存屏障,提供下面4种内存屏障:

loadload:读屏障,load1 loadload load2,保证load2在load1之后执行

storestore:写屏障,store1 storestore store2,保证store2在store1之后执行

loadstore:读写屏障

storeload:写读屏障

我们也能使用Unsafe魔法类封装的方法来手动添加内存屏障。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值