Java并发_volatile实现可见性但不保证原子性

读后感
  1. 介绍了volatile实现可见性的基本原理
  2. 介绍了volatile不能实现原子性的示例,volatile复合操作不能实现原子性,读取值后在自增前改值可能被其它线程读取并修改,自增后刷新值可能会覆盖其它线程修改后的值
  3. 介绍了实现原子性的三种方法及示例
    1. synchronized  修饰对象
    2. ReentrantLock 使用lock()、unlock()加锁解锁,比synchronized功能更多,JDK6后性能和synchronized差不多
    3. AtomicInteger  使用乐观锁


  volatile关键字:

  • 能够保证volatile变量的可见性
  • 不能保证volatile变量复合操作的原子性

  volatile如何实现内存可见性:

         深入来说:通过加入内存屏障和禁止重排序优化来实现的。

  • 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
  • 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令

         通俗地讲:volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存。这样任何时刻,不同的线程总能看到该变量的最新值。

 

         线程写volatile变量的过程:

  • 改变线程工作内存中volatile变量副本的值
  • 将改变后的副本的值从工作内存刷新到主内存

         线程读volatile变量的过程:

  • 从主内存中读取volatile变量的最新值到线程的工作内存中
  • 从工作内存中读取volatile变量的副本

         volatile不能保证volatile变量复合操作的原子性:

Java代码  收藏代码
  1. private int number = 0;  
  2. number++; //不是原子操作  

         它分为三步:
         读取number的值
         将number的值加1
         写入最新的number的值

 

          保证number自增操作的原子性:

  • 使用synchronized关键字
  • 使用ReentrantLock
  • 使用AtomicInteger

          使用synchronized关键字

Java代码  收藏代码
  1. import java.util.concurrent.ExecutorService;  
  2. import java.util.concurrent.Executors;  
  3.   
  4. /** 
  5.  * @author InJavaWeTrust 
  6.  */  
  7. public 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

Java代码  收藏代码
  1. import java.util.concurrent.ExecutorService;  
  2. import java.util.concurrent.Executors;  
  3. import java.util.concurrent.locks.Lock;  
  4. import java.util.concurrent.locks.ReentrantLock;  
  5. /** 
  6.  * @author InJavaWeTrust 
  7.  */  
  8. public 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

Java代码  收藏代码
  1. import java.util.concurrent.ExecutorService;  
  2. import java.util.concurrent.Executors;  
  3. import java.util.concurrent.atomic.AtomicInteger;  
  4.   
  5. /** 
  6.  * @author InJavaWeTrust 
  7.  */  
  8. public 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. }  




posted @ 2016-12-15 09:45    阅读(3021)  评论(0编辑  收藏  举报