共享变量可见性实现的原理
java 语言层面支持的可见性实现方式: synchronized volatile
1、 synchronized 的两条规定:
1 线程解锁前,必须把共享变量的最新值刷新到主内存中。
2 线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(加锁与解锁需要是同一锁)
线程解锁前对共享变量的修改在下次加锁时对其他线程可见。
2、 volatile 实现可见性
深入来说,通过加入内存屏障和禁止重排序优化来实现 的。
对volatile变量执行写操作时,会在写操作后加入一条store屏障指令。
对volatile变量执行读操作时,会在读操作前加入一条load 屏障指令。
通俗地讲,volatile 变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会
强迫线程将最新的值刷新到主内存中,这样任何时刻,不同的线程总能看到该变量的最新值 。
但volatile 不能保证变量的原子性。
典型的num++ 操作,使用volatile修饰变量时,无法保存操作的原子性,所以会出现很多值的情况。
如果出现这种情况,还需要使用volatile时解决办法就是:
使用synchronized 关键字 进行 包装。ReentrantLock 进行代码前的加锁,然后使用unlock 释放锁。
在使用volatile 的时候注意专场合:
3、 synchronized 和 volatile 比较
代码示例:
package org.apple.thread; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class VolatileTest { private Lock lock = new ReentrantLock(); private volatile int number = 0; public void increase(){ this.number++; // 第二种解决办法 // lock.lock(); // this.number++; // lock.unlock(); // 第一种解决办法 //synchronized(this){ // this.number++; //} } public int getNumber(){ return this.number; } /** * @param args */ public static void main(String[] args) { final VolatileTest volatileTest = new VolatileTest(); for (int i = 0; i < 500; i++) { new Thread(new Runnable() { @Override public void run() { volatileTest.increase(); } }).start(); } // 如果当前线程还有子线程在运行,主线程则让出cpu 资源,直到所有的子线程运行完成后,则往下继续执行。 if(Thread.activeCount()>1){ Thread.yield(); } System.out.println(Thread.activeCount()); System.out.println(volatileTest.getNumber()); } }
java 语言多线程可见性(synchronized 和 volatile 学习)
原文:http://www.cnblogs.com/shaoyu19900421/p/4405092.html