Java多线程之内存可见性

volatile不需要加锁,比synchronized更轻量级,不会阻塞线程,所以volatile执行效率更高;
从内存可见性角度看,volatile读相当于加锁,volatile写相当于解锁;
synchronized既能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性;

synchronized能够保证锁内操作的原子性(同步),并且也具备内存可见性的特性

JMM(Java内存管理)关于synchronized的两条规定:

1) 线程解锁前,必须把共享变量的最新值刷新到主内存中
2) 线程加锁时,将清空工作内存中的共享变量的值,
从而使用共享变量时需要从主内存中重新读取最新的值(加锁解锁需要的都是同一把锁)
(ps.线程解锁前对共享变量的修改在下次加锁时对其他线程可见)

.

线程执行互斥代码的过程:

1.获得互斥锁
2.清空工作内存
3.从主内存拷贝变量的最新副本到工作内存
4.执行代码(可能代码会改变共享变量的值)
5.将更改后的共享变量的值刷新到主内存中
6.释放互斥锁

public class Demo { //共享变量 private boolean ready = false; private int result = 0; private int number = 1; //可以猜想一下,读写操作的步骤的顺序,result可能是6或者0 //写操作 public void write() { ready = true; //写操作第一步 number = 2; //写操作第二步 } //读操作 public void read() { if(ready) { //读操作第一步 result = number*3; //读操作第二步 } System.out.println("result的值为:"+result); } //内部线程类 private class ReadWriteThread extends Thread { //根据构造方法中传入的flag参数。确定线程执行读操作还是写操作 private boolean flag; public ReadWriteThread(boolean flag) { this.flag = flag; } public void run() { if(flag) { //构造方法中传入true,执行写操作 write(); } else { //构造方法中传入flase,执行读操作 read(); } } } public static void main(String args[]) { Demo demo = new Demo(); //启动线程执行写操作 demo.new ReadWriteThread(true).start(); //启动线程执行读操作 demo.new ReadWriteThread(false).start(); } } /* 导致共享变量在线程间不可见的原因: 1.线程的交叉执行 2.重排序结合线程交叉执行 3.共享变量更新后的值没有在工作内存与主内存之间及时更新 */

在读写操作方法加入synchronized关键字,使其变成安全的代码:

public synchronized void write() { ready = true; number = 2; } public synchronized void read() { if(ready) { result = number*3; } System.out.println("result的值为:"+result); } /* synchronized相当于加了一把锁,锁内部的代码在一段时间内只能由一个线程执行,除非释放锁 synchronized其原子性,避免了线程的交叉执行的发生; 由于已经避免了线程的交叉执行,所以无论锁内部怎么重排序,都不会对结果造成影响; synchronized其可见性的特性,可以避免共享变量未及时更新; */

还有一种特殊的情况,就是还未执行写操作,先执行读操作,
这种情况也是result为0,但是由于不是因为线程交叉执行而导致的,所以使用synchronized关键字没有作用
避免这种情况的话,可以使用sleep()使其休眠,代码如下:

//修改代码,休眠操作 public static void main(String args[]) { Demo demo = new Demo(); //启动线程执行写操作 demo.new ReadWriteThread(true).start(); try { Thread.sleep(1000); } catch(InterruptedException e) { e.printStackTrace(); } //启动线程执行读操作 demo.new ReadWriteThread(false).start(); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/116628d0e125b5a14487e34f1f2d63e8.html