Java内存模型( JMM ) :
1) 所有的变量都存储在主内存中
2) 每个线程都有自己独立的工作内存, 里面保存该线程使用到的变量的副本 ( 主内存中该变量的一份拷贝 )
JMM 两条规定:
1) 线程对共享变量的所有操作都必须在自己的工作内存中进行
2) 不同线程之间无法直接访问其他线程工作内存中的共享变量, 线程间共享变量值的传递必须通过主内存
线程间共享变量可见性实现的原理:
线程A 对共享变量的修改想被线程B 及时看到, 必须要经过以下2个步骤:
1) 把线程A 工作内存中更新过的共享变量刷新到主内存中( store )
2) 将主内存中最新的共享变量的值共享到线程B 工作内存中( load )
Java 语言层面支持的可见性实现方式:
1) synchronized
2) volatile
JUC 包下的类也可以实现可见性
1) Atomic
2) ReentrantLock
3) Semaphore
1. synchronized 实现可见性
JMM 关于 synchronized 的两条规定:
1) 线程释放锁前, 必须把共享变量的最新值从该线程的工作内存刷新到主内存中
2) 线程持有锁时, 将清空该线程工作内存中共享变量的值, 从主内存中读取最新的值
synchronized 实现可见性的原因:
线程释放锁前对共享变量的修改在下次持有锁时对其他线程可见
public class SynchronizedDemo { // 共享变量 private int result = 0; // 共享变量执行自增操作 public synchronized void increase() { result++; } public int getResult() { return result; } public static void main(String[] args) throws InterruptedException { final SynchronizedDemo demo = new SynchronizedDemo(); // 设置启动500个线程 int count = 500; // 创建一个 JUC 包下的同步同步计数器, 设置计数次数为线程数500 final CountDownLatch cdl = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { demo.increase(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(demo.getResult()); }}
Console 输出: 500
synchronized 不仅可以实现可见性, 还可以实现原子性
2.
volatile 如何实现可见性:
通过加入内存屏障和禁止指令重排序
1) 对 volatile 变量执行写操作时, 会在写操作后加入一条 store 屏障指令
2) 对 volatile 变量执行读操作时, 会在读操作前加入一条 load 屏障指令
public class VolatileDemo { // 使用 volatile 修饰共享变量 private volatile int result = 0; // 共享变量 result 执行自增操作, 无法保证原子性 public void increase() { result++; } public int getResult() { return result; } public static void main(String[] args) throws InterruptedException { final VolatileDemo demo = new VolatileDemo(); // 设置启动500个线程 int count = 500; // 创建一个 JUC 包下的同步同步计数器, 设置计数次数为线程数500 final CountDownLatch cdl = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { demo.increase(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(demo.getResult()); }}
Console 输出: 498
volatile 关键字, 能保证 volatile 变量的可见性, 不能保证 volatile 变量操作的原子性( 如 ++/-- )
3. AtomicInteger 实现可见性
用 Atomic 类实现共享变量在线程中的原子性( )
public class AtomicIntegerDemo { // 共享变量 private AtomicInteger result = new AtomicInteger(0); // 使用 Atomic 类的 incrementAndGet() 方法( 原子操作 ), 实现自增 public void increase() { result.incrementAndGet(); } public int getResult() { return result.get(); } public static void main(String[] args) throws InterruptedException { final AtomicIntegerDemo demo = new AtomicIntegerDemo(); // 设置启动500个线程 int count = 500; // 创建一个JUC包下的同步同步计数器, 设置计数次数为线程数500 final CountDownLatch cdl = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { demo.increase(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(demo.getResult()); }}
Console 输出: 500
4. JUC 包下的 Lock 实现可见性
用 ReentrantLock 实现共享变量在线程中的原子性
public class LockDemo { // 共享变量 private int result = 0; // 可重入锁 private Lock lock = new ReentrantLock(); // 使用锁机制, 保证锁内代码的原子性 public void increase() { // 加锁 lock.lock(); try { result++; } finally { // 释放锁 lock.unlock(); } } public int getResult() { return result; } public static void main(String[] args) throws InterruptedException { final LockDemo demo = new LockDemo(); // 设置启动500个线程 int count = 500; // 创建一个JUC包下的同步同步计数器, 设置计数次数为线程数500 final CountDownLatch cdl = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { demo.increase(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(demo.getResult()); }}
Console 输出: 500
5. Semaphore 实现可见性
用信号量机制实现共享变量在线程中的原子性
public class SemaphoreDemo { // 共享变量 private int result = 0; // 初始化信号量为1, 一次只能有1个线程访问共享变量, 相当于互斥锁 private Semaphore semaphore = new Semaphore(1); // 使用信号量机制, 保证共享变量自增操作的原子性 public void increase() { try { // 获取1个信号量 semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } result++; // 释放1个信号量 semaphore.release(); } public int getResult() { return result; } public static void main(String[] args) throws InterruptedException { final SemaphoreDemo demo = new SemaphoreDemo(); // 设置启动500个线程 int count = 500; // 创建一个JUC包下的同步同步计数器, 设置计数次数为线程数500 final CountDownLatch cdl = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { demo.increase(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(demo.getResult()); }}
Console 输出: 500
总结:
synchronized 代码块具备 可见性 和 可见性
volatile 变量具备可见性, 不具备原子性
http://my.oschina.net/u/2503731/blog/671283