现在的位置: 首页 > 操作系统 > 正文

Java多线程之内存可见性

2020年02月10日 操作系统 ⁄ 共 2010字 ⁄ 字号 评论关闭

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();}

更多详情见请继续阅读下一页的精彩内容: http://www.xuebuyuan.com/Linux/2017-02/141093p2.htm

以上就上有关Java多线程之内存可见性的全部内容,学步园全面介绍编程技术、操作系统、数据库、web前端技术等内容。

抱歉!评论已关闭.