volatile关键字:
- 能够保证volatile变量的可见性
- 不能保证volatile变量复合操作的原子性
volatile如何实现内存可见性:
深入来说:通过加入内存屏障和禁止重排序优化来实现的。
- 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
- 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令
通俗地讲:volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存。这样任何时刻,不同的线程总能看到该变量的最新值。
线程写volatile变量的过程:
- 改变线程工作内存中volatile变量副本的值
- 将改变后的副本的值从工作内存刷新到主内存
线程读volatile变量的过程:
- 从主内存中读取volatile变量的最新值到线程的工作内存中
- 从工作内存中读取volatile变量的副本
volatile不能保证volatile变量复合操作的原子性:
1private int number = 0; 2number++; //不是原子操作
它分为三步:
读取number的值
将number的值加1
写入最新的number的值
保证number自增操作的原子性:
- 使用synchronized关键字
- 使用ReentrantLock
- 使用AtomicInteger
使用synchronized关键字
1import java.util.concurrent.ExecutorService; 2import java.util.concurrent.Executors; 3 4/** 5 * @author InJavaWeTrust 6 */ 7public class TestSyn implements Runnable { 8 9 private int number = 0; 10 11 public int getNumber() { 12 return this.number; 13 } 14 15 public void run() { 16 increase(); 17 } 18 19 public void increase() { 20 synchronized (this) { 21 this.number++; 22 } 23 } 24 25 public static void main(String[] args) { 26 ExecutorService exec = Executors.newFixedThreadPool(1000); 27 TestSyn syn = new TestSyn(); 28 for (int i = 0; i < 1000; i++) { 29 exec.submit(syn); 30 } 31 System.out.println("number : " + syn.getNumber()); 32 exec.shutdown(); 33 } 34}
使用ReentrantLock
1import java.util.concurrent.ExecutorService; 2import java.util.concurrent.Executors; 3import java.util.concurrent.locks.Lock; 4import java.util.concurrent.locks.ReentrantLock; 5/** 6 * @author InJavaWeTrust 7 */ 8public class TestRee implements Runnable { 9 10 private Lock lock = new ReentrantLock(); 11 private int number = 0; 12 13 public int getNumber() { 14 return this.number; 15 } 16 17 public void run() { 18 increase(); 19 } 20 21 public void increase() { 22 lock.lock(); 23 try { 24 this.number++; 25 } finally { 26 lock.unlock(); 27 } 28 } 29 30 public static void main(String[] args) { 31 TestRee ree = new TestRee(); 32 ExecutorService exec = Executors.newFixedThreadPool(1000); 33 for (int i = 0; i < 1000; i++) { 34 exec.submit(ree); 35 } 36 System.out.println("number : " + ree.getNumber()); 37 exec.shutdown(); 38 } 39}
使用AtomicInteger
1import java.util.concurrent.ExecutorService; 2import java.util.concurrent.Executors; 3import java.util.concurrent.atomic.AtomicInteger; 4 5/** 6 * @author InJavaWeTrust 7 */ 8public class TestAtomic implements Runnable { 9 10 private static AtomicInteger number = new AtomicInteger(0); 11 12 public void run() { 13 increase(); 14 } 15 16 public void increase() { 17 number.getAndAdd(1); 18 } 19 20 public static void main(String[] args) { 21 TestAtomic ato = new TestAtomic(); 22 ExecutorService exec = Executors.newFixedThreadPool(1000); 23 for (int i = 0; i < 1000; i++) { 24 exec.submit(ato); 25 } 26 System.out.println("number : " + number.get()); 27 exec.shutdown(); 28 } 29}