ReentrantLock與synchronized

關於互斥鎖:javascript

所謂互斥鎖, 指的是一次最多隻能有一個線程持有的鎖. 在jdk1.5以前, 咱們一般使用synchronized機制控制多個線程對共享資源的訪問. 而如今, Lock提供了比synchronized機制更普遍的鎖定操做, Lock和synchronized機制的主要區別:html

synchronized機制提供了對與每一個對象相關的隱式監視器鎖的訪問, 並強制全部鎖獲取和釋放均要出如今一個塊結構中, 當獲取了多個鎖時, 它們必須以相反的順序釋放. synchronized機制對鎖的釋放是隱式的, 只要線程運行的代碼超出了synchronized語句塊範圍, 鎖就會被釋放. 而Lock機制必須顯式的調用Lock對象的unlock()方法才能釋放鎖, 這爲獲取鎖和釋放鎖不出如今同一個塊結構中, 以及以更自由的順序釋放鎖提供了可能. java

 

 

關於可重入: api

1、2.4.1 內部鎖多線程

Java 提供了原子性的內置鎖機制: sychronized 塊。它包含兩個部分:鎖對象的引用和這個鎖保護的代碼塊:app

synchronized(lock) {ide

// 訪問或修改被鎖保護的共享狀態性能

}單元測試

內部鎖扮演了互斥鎖( mutual exclusion lock, 也稱做 mutex )的角色,一個線程擁有鎖的時候,別的線程阻塞等待。測試

 

2.4.2 重進入(Reentrancy )

重入性:指的是同一個線程屢次試圖獲取它所佔有的鎖,請求會成功。當釋放鎖的時候,直到重入次數清零,鎖才釋放完畢。

Public class Widget {

      Public synchronized void doSomething(){

           …

      }

}

Public class LoggingWidget extends Widget {

   Public synchronized void doSomething(){

      System.out.println(toString()+」:calling doSomething」);

      Super.doSomething();

   }

}

 

2、通常來講,在多線程程序中,某個任務在持有某對象的鎖後才能運行任務,其餘任務只有在該任務釋放同一對象鎖後才能擁有對象鎖,而後執行任務。因而,想到,同一個任務在持有同一個對象的鎖後,在不釋放鎖的狀況下,繼續調用同一個對象的其餘同步(synchronized)方法,該任務是否會再次持有該對象鎖呢? 

    答案是確定的。同一個任務在調用同一個對象上的其餘synchronized方法,能夠再次得到該對象鎖。 

 

Java代碼 

 收藏代碼

  1. synchronized  m1(){  
  2. //加入此時對鎖a的計數是N  
  3.  m2();  //進入m2的方法體以後鎖計數是N+1,離開m2後是N  
  4. }  
  5. synchronized m2(){}  

 同一任務和對象鎖的問題:http://www.iteye.com/topic/728485

 

 

 

Java代碼 

 收藏代碼

  1. /*public class ReentrantLock  
  2. extends Object implements Lock, Serializable 
  3. */  

  

一個可重入的互斥鎖 Lock,它具備與使用 synchronized 方法和語句所訪問的隱式監視器鎖相同的一些基本行爲和語義,但功能更強大。

 

ReentrantLock 將由最近成功得到鎖,而且尚未釋放該鎖的線程所擁有。當鎖沒有被另外一個線程所擁有時,調用 lock 的線程將成功獲取該鎖並返回。若是當前線程已經擁有該鎖,此方法將當即返回。可使用 isHeldByCurrentThread() 和 getHoldCount() 方法來檢查此狀況是否發生。

 

此類的構造方法接受一個可選的公平 參數。當設置爲 true 時,在多個線程的爭用下,這些鎖傾向於將訪問權授予等待時間最長的線程。不然此鎖將沒法保證任何特定訪問順序。與採用默認設置(使用不公平鎖)相比,使用公平鎖的程序在許多線程訪問時表現爲很低的整體吞吐量(即速度很慢,經常極其慢),可是在得到鎖和保證鎖分配的均衡性時差別較小。不過要注意的是,公平鎖不能保證線程調度的公平性。所以,使用公平鎖的衆多線程中的一員可能得到多倍的成功機會,這種狀況發生在其餘活動線程沒有被處理而且目前並未持有鎖時。還要注意的是,未定時的 tryLock 方法並無使用公平設置。由於即便其餘線程正在等待,只要該鎖是可用的,此方法就能夠得到成功。

JDK:http://www.xasxt.com/java/api/java/util/concurrent/locks/ReentrantLock.html

 

 

Java代碼 

 收藏代碼

  1. /*構造方法摘要 
  2. ReentrantLock()  
  3.           建立一個 ReentrantLock 的實例。 
  4. ReentrantLock(boolean fair)  
  5.           建立一個具備給定公平策略的 ReentrantLock。 
  6. */  

  

Java代碼 

 收藏代碼

  1. /**public void lock() 
  2. 獲取鎖。 
  3. 若是該鎖沒有被另外一個線程保持,則獲取該鎖並當即返回,將鎖的保持計數設置爲 1。 
  4. 若是當前線程已經保持該鎖,則將保持計數加 1,而且該方法當即返回。 
  5. 若是該鎖被另外一個線程保持,則出於線程調度的目的,禁用當前線程,而且在得到鎖以前,該線程將一直處於休眠狀態,此時鎖保持計數被設置爲 1。 
  6. */  

  

ReentrantLock 的lock機制有2種,忽略中斷鎖和響應中斷鎖,這給咱們帶來了很大的靈活性。好比:若是A、B 2個線程去競爭鎖,A線程獲得了鎖,B線程等待,可是A線程這個時候實在有太多事情要處理,就是 一直不返回,B線程可能就會等不及了,想中斷本身,再也不等待這個鎖了,轉而處理其餘事情。這個時候ReentrantLock就提供了2種機制,第一,B線程中斷本身(或者別的線程中斷它),可是ReentrantLock 不去響應,繼續讓B線程等待,你再怎麼中斷,我全當耳邊風(synchronized原語就是如此);第二,B線程中斷本身(或者別的線程中斷它),ReentrantLock 處理了這個中斷,而且再也不等待這個鎖的到來,徹底放棄。請看例子:

Example1:

 

Java代碼 

 收藏代碼

  1. package test;  
  2.   
  3. public interface IBuffer {  
  4.     public void write();  
  5.     public void read() throws InterruptedException;  
  6. }  

使用Synchronized:

Java代碼 

 收藏代碼

  1. package test;  
  2.   
  3. public class Buffer implements IBuffer {  
  4.   
  5.     private Object lock;  
  6.   
  7.     public Buffer() {  
  8.         lock = this;  
  9.     }  
  10.   
  11.     public void write() {  
  12.         synchronized (lock) {  
  13.             long startTime = System.currentTimeMillis();  
  14.             System.out.println("開始往這個buff寫入數據…");  
  15.             for (;;)// 模擬要處理很長時間  
  16.             {  
  17.                 if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE)  
  18.                     break;  
  19.             }  
  20.             System.out.println("終於寫完了");  
  21.         }  
  22.     }  
  23.   
  24.     public void read() {  
  25.         synchronized (lock) {  
  26.             System.out.println("從這個buff讀數據");  
  27.         }  
  28.     }  
  29.   
  30. }  

   使用ReentrantLock:

Java代碼 

 收藏代碼

  1. package test;  
  2. import java.util.concurrent.locks.ReentrantLock;  
  3. public class BufferInterruptibly implements IBuffer {  
  4.   
  5.     private ReentrantLock lock = new ReentrantLock();  
  6.   
  7.     public void write() {  
  8.         lock.lock();  
  9.         try {  
  10.             long startTime = System.currentTimeMillis();  
  11.             System.out.println("開始往這個buff寫入數據…");  
  12.             for (;;)// 模擬要處理很長時間  
  13.             {  
  14.                 if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE)  
  15.                     break;  
  16.             }  
  17.             System.out.println("終於寫完了");  
  18.         } finally {  
  19.             lock.unlock();  
  20.         }  
  21.     }  
  22.   
  23.     public void read() throws InterruptedException{  
  24.         lock.lockInterruptibly();// 注意這裏,能夠響應中斷  
  25.         try {  
  26.             System.out.println("從這個buff讀數據");  
  27.         } finally {  
  28.             lock.unlock();  
  29.         }  
  30.     }  
  31.   
  32. }  

  

測試類(注意那兩個線程不是內部類!):

Java代碼 

 收藏代碼

  1. package test;  
  2.   
  3. public class Test {  
  4.      //是用ReentrantLock,仍是用synchronized  
  5.     public static boolean useSynchronized = false;  
  6.     public static void main(String[] args) {  
  7.         IBuffer buff = null;  
  8.         if(useSynchronized){  
  9.             buff = new Buffer();  
  10.         }else{  
  11.             buff = new BufferInterruptibly();      
  12.         }  
  13.         final Writer writer = new Writer(buff);  
  14.         final Reader reader = new Reader(buff);  
  15.         writer.start();  
  16.         reader.start();  
  17.         new Thread(new Runnable() {  
  18.             public void run() {  
  19.                 long start = System.currentTimeMillis();  
  20.                 for (;;) {  
  21.                     // 等5秒鐘去中斷讀  
  22.                     if (System.currentTimeMillis() - start > 5000) {  
  23.                         System.out.println("不等了,嘗試中斷");  
  24.                         reader.interrupt();  
  25.                         break;  
  26.                     }  
  27.   
  28.                 }  
  29.   
  30.             }  
  31.         }).start();  
  32.     }      
  33. }  
  34.   
  35.     class Writer extends Thread {     
  36.         private IBuffer buff;  
  37.       
  38.         public Writer(IBuffer buff) {  
  39.             this.buff = buff;  
  40.         }  
  41.       
  42.         @Override  
  43.         public void run() {  
  44.             buff.write();  
  45.         }  
  46.     }  
  47.       
  48.     class Reader extends Thread {  
  49.         private IBuffer buff;  
  50.         public Reader(IBuffer buff) {  
  51.             this.buff = buff;  
  52.         }  
  53.         @Override  
  54.         public void run() {  
  55.             try {  
  56.                 buff.read();  
  57.             } catch (InterruptedException e) {  
  58.                 System.out.println("我不讀了");     
  59.             }  
  60.             System.out.println("讀結束");  
  61.         }  
  62.     }  

 結果:

使用ReentrantLock時:

開始往這個buff寫入數據…

不等了,嘗試中斷

我不讀了

讀結束

 

使用Synchronized時:

開始往這個buff寫入數據…

不等了,嘗試中斷

實例來源:http://blog.csdn.net/quqi99/article/details/5298017

 

 

實例2:

http://junlas.iteye.com/blog/846460

實例3:

http://www.blogjava.net/killme2008/archive/2007/09/14/145195.html

 

 

重要:

一個證實可中斷的例子:http://yanxuxin.iteye.com/blog/566713

關於多線程問題,signalAll,await問題:http://www.iteye.com/problems/72378

ReentrantLock :http://hujin.iteye.com/blog/479689

 

java的concurrent用法詳解:

http://www.open-open.com/bbs/view/1320131360999

 

ReentrantLock-互斥同步器:

http://www.cnblogs.com/mandela/archive/2011/04/08/2009810.html

 

一個重要Example:

 

Java代碼 

 收藏代碼

  1. package tags;  
  2.   
  3. import java.util.Calendar;  
  4.   
  5. public class TestLock {  
  6.     private ReentrantLock lock = null;  
  7.       
  8.     public int data = 100;     // 用於線程同步訪問的共享數據  
  9.   
  10.     public TestLock() {  
  11.         lock = new ReentrantLock(); // 建立一個自由競爭的可重入鎖  
  12.     }  
  13.     public ReentrantLock getLock() {  
  14.         return lock;  
  15.     }  
  16.       
  17.     public void testReentry() {  
  18.         lock.lock();  
  19.         Calendar now = Calendar.getInstance();  
  20.         System.out.println(now.getTime() + " " + Thread.currentThread() + " get lock.");  
  21.     }  
  22.   
  23.     public static void main(String[] args) {  
  24.         TestLock tester = new TestLock();  
  25.   
  26.         //一、測試可重入  
  27.         tester.testReentry();  
  28.         tester.testReentry(); // 能執行到這裏而不阻塞,表示鎖可重入  
  29.         tester.testReentry(); // 再次重入  
  30.   
  31.         // 釋放重入測試的鎖,要按重入的數量解鎖,不然其餘線程沒法獲取該鎖。  
  32.         tester.getLock().unlock();  
  33.         tester.getLock().unlock();  
  34.         tester.getLock().unlock();  
  35.   
  36.         //二、測試互斥  
  37.         // 啓動3個線程測試在鎖保護下的共享數據data的訪問  
  38.         new Thread(new workerThread(tester)).start();  
  39.         new Thread(new workerThread(tester)).start();  
  40.         new Thread(new workerThread(tester)).start();  
  41.     }  
  42.   
  43.   
  44.     // 線程調用的方法  
  45.     public void testRun() throws Exception {  
  46.         lock.lock();  
  47.   
  48.         Calendar now = Calendar.getInstance();  
  49.         try {  
  50.             // 獲取鎖後顯示 當前時間 當前調用線程 共享數據的值(並使共享數據 + 1)  
  51.             System.out.println(now.getTime() + " " + Thread.currentThread()+ " accesses the data " + data++);  
  52.             Thread.sleep(1000);  
  53.         } catch (Exception e) {  
  54.             e.printStackTrace();  
  55.         } finally {  
  56.             lock.unlock();  
  57.         }  
  58.     }  
  59. }  
  60.   
  61. // 工做線程,調用TestServer.testRun  
  62. class workerThread implements Runnable {  
  63.   
  64.     private TestLock tester = null;  
  65.   
  66.     public workerThread(TestLock testLock) {  
  67.         this.tester = testLock;  
  68.     }  
  69.   
  70.     public void run() {  
  71.         try {  
  72.             tester.testRun();  
  73.         } catch (Exception e) {  
  74.             e.printStackTrace();  
  75.         }  
  76.     }  
  77. }  

Example3:

 

Java代碼 

 收藏代碼

  1. package tags;  
  2. import java.util.concurrent.locks.ReentrantLock;  
  3.   
  4. public class ReentrantLockSample {  
  5.   
  6.     public static void main(String[] args) {  
  7.         testSynchronized();  
  8.         //testReentrantLock();  
  9.     }  
  10.   
  11.     public static void testReentrantLock() {  
  12.         final SampleSupport1 support = new SampleSupport1();  
  13.         Thread first = new Thread(new Runnable() {  
  14.             public void run() {  
  15.                 try {  
  16.                     support.doSomething();  
  17.                 }  
  18.                 catch (InterruptedException e) {  
  19.                     e.printStackTrace();  
  20.                 }  
  21.             }  
  22.         });  
  23.   
  24.         Thread second = new Thread(new Runnable() {  
  25.             public void run() {  
  26.                 try {  
  27.                     support.doSomething();  
  28.                 }  
  29.                 catch (InterruptedException e) {  
  30.                     System.out.println("Second Thread Interrupted without executing counter++,beacuse it waits a long time.");  
  31.                 }  
  32.             }  
  33.         });  
  34.   
  35.         executeTest(first, second);  
  36.     }  
  37.   
  38.     public static void testSynchronized() {  
  39.         final SampleSupport2 support2 = new SampleSupport2();  
  40.   
  41.         Runnable runnable = new Runnable() {  
  42.             public void run() {  
  43.                 support2.doSomething();  
  44.             }  
  45.         };  
  46.   
  47.         Thread third = new Thread(runnable);  
  48.         Thread fourth = new Thread(runnable);  
  49.   
  50.         executeTest(third, fourth);  
  51.     }  
  52.   
  53.     /** 
  54.      * Make thread a run faster than thread b, 
  55.      * then thread b will be interruted after about 1s. 
  56.      * @param a 
  57.      * @param b 
  58.      */  
  59.     public static void executeTest(Thread a, Thread b) {  
  60.         a.start();  
  61.         try {  
  62.             Thread.sleep(100);  
  63.             b.start(); // The main thread sleep 100ms, and then start the second thread.  
  64.   
  65.             Thread.sleep(1000);  
  66.     // 1s later, the main thread decided not to allow the second thread wait any longer.  
  67.             b.interrupt();   
  68.         }  
  69.         catch (InterruptedException e) {  
  70.             e.printStackTrace();  
  71.         }  
  72.     }  
  73. }  
  74.   
  75. abstract class SampleSupport {  
  76.   
  77.     protected int counter;  
  78.   
  79.     /** 
  80.      * A simple countdown,it will stop after about 5s.  
  81.      */  
  82.     public void startTheCountdown() {  
  83.         long currentTime = System.currentTimeMillis();  
  84.         for (;;) {  
  85.             long diff = System.currentTimeMillis() - currentTime;  
  86.             if (diff > 5000) {  
  87.                 break;  
  88.             }  
  89.         }  
  90.     }  
  91. }  
  92.   
  93. class SampleSupport1 extends SampleSupport {  
  94.   
  95.     private final ReentrantLock lock = new ReentrantLock();  
  96.   
  97.     public void doSomething() throws InterruptedException {  
  98.         lock.lockInterruptibly(); // (1)  
  99.         System.out.println(Thread.currentThread().getName() + " will execute counter++.");  
  100.         startTheCountdown();  
  101.         try {  
  102.             counter++;  
  103.         }  
  104.         finally {  
  105.             lock.unlock();  
  106.         }  
  107.     }  
  108. }  
  109.   
  110. class SampleSupport2 extends SampleSupport {  
  111.   
  112.     public synchronized void doSomething() {  
  113.         System.out.println(Thread.currentThread().getName() + " will execute counter++.");  
  114.         startTheCountdown();  
  115.         counter++;  
  116.     }  
  117. }  

 在這個例子中,輔助類SampleSupport提供一個倒計時的功能startTheCountdown(),這裏倒計時5s左右。SampleSupport1,SampleSupport2繼承其並分別的具備doSomething()方法,任何進入方法的線程會運行5s左右以後counter++而後離開方法釋放鎖。SampleSupport1是使用ReentrantLock機制,SampleSupport2是使用synchronized機制。 


    testSynchronized()和testReentrantLock()都分別開啓兩個線程執行測試方法executeTest(),這個方法會讓一個線程先啓動,另外一個過100ms左右啓動,而且隔1s左右試圖中斷後者。結果正如以前提到的第二點:interrupt()對於synchronized是沒有做用的,它依然會等待5s左右得到鎖執行counter++;而ReentrantLock機制能夠保證在線程還未得到而且試圖得到鎖時若是發現線程中斷,則拋出異常清除中斷標記退出競爭。因此testReentrantLock()中second線程不會繼續去競爭鎖,執行異常內的打印語句後線程運行結束。 

來源:http://yanxuxin.iteye.com/blog/566713

 

Example4:

三個線程,線程名分別爲A、B、C,設計程序使得三個線程循環打印「ABC」10次後終止。如:ABCABCABCABCABCABCABCABCABCABC

 

Java代碼 

 收藏代碼

  1. package tags;  
  2.   
  3. import java.util.concurrent.locks.ReentrantLock;  
  4.   
  5. public class ReentrantLockPractice {  
  6.   
  7.     static ReentrantLock lock = new ReentrantLock();  
  8.     private static String[] threadArr = {"A","B","C"};  
  9.       
  10.     public static void main(String[] args){  
  11.         ReentrantLockPractice pc = new ReentrantLockPractice();  
  12.         pc.startDemo();  
  13.     }  
  14.       
  15.     void startDemo(){  
  16.         for(int i = 0;i<10;i++){  
  17.             for(String name : threadArr){  
  18.                 TestThread t = new TestThread(name);  
  19.                 t.start();  
  20.                 try {  
  21.                     Thread.sleep(100);  
  22.                 } catch (InterruptedException e) {  
  23.                     e.printStackTrace();  
  24.                 }  
  25.             }  
  26.         }  
  27.     }  
  28.       
  29.   
  30.     class TestThread extends Thread{  
  31.           
  32.         //自定義線程名字  
  33.         TestThread(String str){  
  34.             super(str);           
  35.         }  
  36.           
  37.         public void run(){  
  38.             try {  
  39.                 lock.lockInterruptibly();  
  40.                 System.out.print(Thread.currentThread().getName());  
  41.             } catch (InterruptedException e) {  
  42.                 e.printStackTrace();  
  43.             } finally{  
  44.                 lock.unlock();  
  45.             }     
  46.         }  
  47.     }  
  48.       
  49. }  

 注意與Example2的區別,一個線材類定義在內部,一個在外部,注意區別。

 其餘方法:

http://hxraid.iteye.com/blog/607228

 

 

 

 

 

相同:ReentrantLock提供了synchronized相似的功能和內存語義。

 

不一樣:

1.ReentrantLock功能性方面更全面,好比時間鎖等候,可中斷鎖等候,鎖投票等,所以更有擴展性。在多個條件變量和高度競爭鎖的地方,用ReentrantLock更合適,ReentrantLock還提供了Condition,對線程的等待和喚醒等操做更加靈活,一個ReentrantLock能夠有多個Condition實例,因此更有擴展性。

2.ReentrantLock必須在finally中釋放鎖,不然後果很嚴重,編碼角度來講使用synchronized更加簡單,不容易遺漏或者出錯。

3.ReentrantLock 的性能比synchronized會好點。

4.ReentrantLock提供了可輪詢的鎖請求,他能夠嘗試的去取得鎖,若是取得成功則繼續處理,取得不成功,能夠等下次運行的時候處理,因此不容易產生死鎖,而synchronized則一旦進入鎖請求要麼成功,要麼一直阻塞,因此更容易產生死鎖。

 

一、Lock的某些方法能夠決定多長時間內嘗試獲取鎖,若是獲取不到就拋異常,這樣就能夠必定程度上減輕死鎖的可能性。

若是鎖被另外一個線程佔據了,synchronized只會一直等待,很容易錯序死鎖 

二、synchronized的話,鎖的範圍是整個方法或synchronized塊部分;而Lock由於是方法調用,能夠跨方法,靈活性更大 

三、便於測試,單元測試時,能夠模擬Lock,肯定是否得到了鎖,而synchronized就沒辦法了

 

 

ReentrantLock比synchronized 強大在哪兒?

簡單說: 

一、ReentrantLock能夠實現fair lock 

 

public ReentrantLock(boolean fair) {   

    sync = (fair)? new FairSync() : new NonfairSync();  

}  

所謂fair lock就是看得到鎖的順序是否是和申請鎖的時間的順序是一致的 

 

二、ReentrantLock支持中斷處理 

 

public final void acquireInterruptibly(int arg) throws InterruptedException {  

    if (Thread.interrupted())  

        throw new InterruptedException();  

    if (!tryAcquire(arg))  

        doAcquireInterruptibly(arg);  

}  

就是說那些持有鎖的線程一直不釋放,正在等待的線程能夠放棄等待。 

 

三、ReentrantLock能夠和condition結合使用 

 

public boolean hasWaiters(Condition condition) {  

    if (condition == null)  

        throw new NullPointerException();  

    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))  

        throw new IllegalArgumentException("not owner");  

    return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);  

}  

 

public int getWaitQueueLength(Condition condition) {  

    if (condition == null)  

        throw new NullPointerException();  

    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))  

        throw new IllegalArgumentException("not owner");  

    return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);  

}  

 

內置鎖synchronized

顯式鎖Lock

相關文章
相關標籤/搜索