Java中使用Lock控制線程同步

緊接着Callable和線程池,再次接觸java.util.concurrent併發包下的東西。Lock提供比synchronized更靈活的併發控制。Lock是java.util.concurrent.locks包下的接口,Lock 實現提供了比使用synchronized 方法和語句可獲得的更廣泛的鎖定操作,它能以更優雅的方式處理線程同步問題。使用最多的Lock類是ReentrantLock。下面用它來實現上一篇日誌中的打印機的例子:

Java代碼  收藏代碼
  1. import java.util.concurrent.locks.Lock;  
  2. import java.util.concurrent.locks.ReentrantLock;  
  3.    
  4. public class Printer  {   
  5.     private Lock lock = new ReentrantLock();// 鎖對象     
  6.     public void printLetters(char c) {  
  7.          lock.lock();// 得到鎖    
  8.          try {  
  9.              for(int i = 0; i<5; i++) {    
  10.                  System.out.print(c);    
  11.              }    
  12.              System.out.println();  
  13.         }finally {    
  14.              lock.unlock();// 釋放鎖     
  15.         }   
  16.     }  
  17. }  

 這樣就實現了和sychronized一樣的同步效果,值得一提的是,用sychronized修飾的方法或者語句塊在代碼執行完之後鎖自動釋放,而是用Lock需要我們手動釋放鎖,所以爲了保證鎖最終被釋放(發生異常情況),要把互斥區放在try內,釋放鎖放在finally內。

讀鎖和寫鎖的使用:

以下內容轉載來至:http://blog.csdn.net/ghsau/article/details/7461369

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

如果說這就是Lock,那麼它不能成爲同步問題更完美的處理方式,下面要介紹的是讀寫鎖(ReadWriteLock),我們會有一種需求,在對數據進行讀寫的時候,爲了保證數據的一致性和完整性,需要讀和寫是互斥的,寫和寫是互斥的,但是讀和讀是不需要互斥的,這樣讀和讀不互斥性能更高些,來看一下不考慮互斥情況的代碼原型:

Java代碼  收藏代碼
  1. public class ReadWriteLockTest {    
  2.     public static void main(String[] args) {    
  3.         final Data data = new Data();    
  4.         for (int i = 0; i < 3; i++) {    
  5.             new Thread(new Runnable() {    
  6.                 public void run() {    
  7.                     for (int j = 0; j < 5; j++) {    
  8.                         data.set(new Random().nextInt(30));    
  9.                     }    
  10.                 }    
  11.             }).start();    
  12.         }           
  13.         for (int i = 0; i < 3; i++) {    
  14.             new Thread(new Runnable() {    
  15.                 public void run() {    
  16.                     for (int j = 0; j < 5; j++) {    
  17.                         data.get();    
  18.                     }    
  19.                 }    
  20.             }).start();    
  21.         }    
  22.     }    
  23. }    
  24. class Data {        
  25.     private int data;// 共享數據         
  26.     public void set(int data) {    
  27.         System.out.println(Thread.currentThread().getName() + "準備寫入數據");    
  28.         try {    
  29.             Thread.sleep(20);    
  30.         } catch (InterruptedException e) {    
  31.             e.printStackTrace();    
  32.         }    
  33.         this.data = data;    
  34.         System.out.println(Thread.currentThread().getName() + "寫入" + this.data);    
  35.     }       
  36.     public void get() {    
  37.         System.out.println(Thread.currentThread().getName() + "準備讀取數據");    
  38.         try {    
  39.             Thread.sleep(20);    
  40.         } catch (InterruptedException e) {    
  41.             e.printStackTrace();    
  42.         }    
  43.         System.out.println(Thread.currentThread().getName() + "讀取" + this.data);    
  44.     }    
  45. }   

 部分輸出結果:

Text代碼  收藏代碼
  1. Thread-1準備寫入數據    
  2. Thread-3準備讀取數據    
  3. Thread-2準備寫入數據    
  4. Thread-0準備寫入數據    
  5. Thread-4準備讀取數據    
  6. Thread-5準備讀取數據    
  7. Thread-2寫入12    
  8. Thread-4讀取12    
  9. Thread-5讀取5    
  10. Thread-1寫入12    

 我們要實現寫入和寫入互斥,讀取和寫入互斥,讀取和讀取互斥,在set和get方法加入sychronized修飾符:

Java代碼  收藏代碼
  1. public synchronized void set(int data) {...}        
  2. public synchronized void get() {...}    

 部分輸出結果:

Text代碼  收藏代碼
  1. Thread-0準備寫入數據  
  2. Thread-0寫入9  
  3. Thread-5準備讀取數據  
  4. Thread-5讀取9  
  5. Thread-5準備讀取數據  
  6. Thread-5讀取9  
  7. Thread-5準備讀取數據  
  8. Thread-5讀取9  
  9. Thread-5準備讀取數據  
  10. Thread-5讀取9  

 我們發現,雖然寫入和寫入互斥了,讀取和寫入也互斥了,但是讀取和讀取之間也互斥了,不能併發執行,效率較低,用讀寫鎖實現代碼如下:

Java代碼  收藏代碼
  1. class Data {        
  2.     private int data;// 共享數據     
  3.     private ReadWriteLock rwl = new ReentrantReadWriteLock();       
  4.     public void set(int data) {    
  5.         rwl.writeLock().lock();// 取到寫鎖     
  6.         try {    
  7.             System.out.println(Thread.currentThread().getName() + "準備寫入數據");    
  8.             try {    
  9.                 Thread.sleep(20);    
  10.             } catch (InterruptedException e) {    
  11.                 e.printStackTrace();    
  12.             }    
  13.             this.data = data;    
  14.             System.out.println(Thread.currentThread().getName() + "寫入" + this.data);    
  15.         } finally {    
  16.             rwl.writeLock().unlock();// 釋放寫鎖     
  17.         }    
  18.     }       
  19.     public void get() {    
  20.         rwl.readLock().lock();// 取到讀鎖     
  21.         try {    
  22.             System.out.println(Thread.currentThread().getName() + "準備讀取數據");    
  23.             try {    
  24.                 Thread.sleep(20);    
  25.             } catch (InterruptedException e) {    
  26.                 e.printStackTrace();    
  27.             }    
  28.             System.out.println(Thread.currentThread().getName() + "讀取" + this.data);    
  29.         } finally {    
  30.             rwl.readLock().unlock();// 釋放讀鎖     
  31.         }    
  32.     }    
  33. }    

 部分輸出結果爲:

Text代碼  收藏代碼
  1. Thread-4準備讀取數據    
  2. Thread-3準備讀取數據    
  3. Thread-5準備讀取數據    
  4. Thread-5讀取18    
  5. Thread-4讀取18    
  6. Thread-3讀取18    
  7. Thread-2準備寫入數據    
  8. Thread-2寫入6    
  9. Thread-2準備寫入數據    
  10. Thread-2寫入10    
  11. Thread-1準備寫入數據    
  12. Thread-1寫入22    
  13. Thread-5準備讀取數據   

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章