Java線程(八)----鎖對象Lock-同步問題更完美的處理方式

   Lock是Java.util.concurrent.locks包下的接口,Lock 實現提供了比使用synchronized 方法和語句可獲得的更廣泛的鎖定操作,它能以更優雅的方式處理線程同步問題,我們拿Java線程(二)中的一個例子簡單的實現一下和sychronized一樣的效果,代碼如下:

[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class LockTest {  
  2.     public static void main(String[] args) {  
  3.         final Outputter1 output = new Outputter1();  
  4.         new Thread() {  
  5.             public void run() {  
  6.                 output.output("zhangsan");  
  7.             };  
  8.         }.start();        
  9.         new Thread() {  
  10.             public void run() {  
  11.                 output.output("lisi");  
  12.             };  
  13.         }.start();  
  14.     }  
  15. }  
  16. class Outputter1 {  
  17.     private Lock lock = new ReentrantLock();// 鎖對象  
  18.     public void output(String name) {  
  19.         // TODO 線程輸出方法  
  20.         lock.lock();// 得到鎖  
  21.         try {  
  22.             for(int i = 0; i < name.length(); i++) {  
  23.                 System.out.print(name.charAt(i));  
  24.             }  
  25.         } finally {  
  26.             lock.unlock();// 釋放鎖  
  27.         }  
  28.     }  
  29. }  
        這樣就實現了和sychronized一樣的同步效果,需要注意的是,用sychronized修飾的方法或者語句塊在代碼執行完之後鎖自動釋放,而用Lock需要我們手動釋放鎖,所以爲了保證鎖最終被釋放(發生異常情況),要把互斥區放在try內,釋放鎖放在finally內。

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

[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  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. }  
        部分輸出結果:

[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  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] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public synchronized void set(int data) {...}      
  2. public synchronized void get() {...}  
        部分輸出結果:
[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  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] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  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. }  

        部分輸出結果:

[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  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準備讀取數據  

        從結果可以看出實現了我們的需求,這只是鎖的基本用法,鎖的機制還需要繼續深入學習。

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