Java多執行緒——鎖
Java多線系列文章是Java多執行緒的詳解介紹,對多執行緒還不熟悉的同學可以先去看一下我的這篇部落格Java基礎系列3:多執行緒超詳細總結,這篇部落格從巨集觀層面介紹了多執行緒的整體概況,接下來的幾篇文章是對多執行緒的深入剖析。
Lock鎖
1、簡介
1、從Java5開始,Java提供了一種功能更強大的執行緒同步機制——通過顯式定義同步鎖物件來實現同步,在這種機制下,同步鎖由Lock物件充當。
2、Lock 提供了比synchronized方法和synchronized程式碼塊更廣泛的鎖定操作,Lock允許實現更靈活的結構,可以具有差別很大的屬性,並且支援多個相關的Condition物件。
3、Lock是控制多個執行緒對共享資源進行訪問的工具。通常,鎖提供了對共享資源的獨佔訪問,每次只能有一個執行緒對Lock物件加鎖,執行緒開始訪問共享資源之前應先獲得Lock物件。
4、某些鎖可能允許對共享資源併發訪問,如ReadWriteLock(讀寫鎖),Lock、ReadWriteLock是Java5提供的兩個根介面,併為Lock 提供了ReentrantLock(可重入鎖)實現類,為ReadWriteLock提供了ReentrantReadWriteLock 實現類。
5、Java8新增了新型的StampedLock類,在大多數場景中它可以替代傳統的ReentrantReadWriteLock。ReentrantReadWriteLock 為讀寫操作提供了三種鎖模式:Writing、ReadingOptimistic、Reading。
2、Lock鎖使用
class X{ //定義鎖物件 private final ReentrantLock lock=new ReentrantLock(); //定義需要保證執行緒安全的方法 public void m() { //加鎖 lock.lock(); try { //需要保證執行緒安全的程式碼 } finally { lock.unlock(); } } }
ReentranLock
1、簡介
在Java多執行緒中,可以使用synchronized關鍵字來實現執行緒之間同步互斥,但在JDK1.5中新增加了ReentrantLock類也能達到同樣的效果,並且在擴充套件功能上也更加強大,比如具有嗅探鎖定、多路分支通知等功能,而且在使用上也比synchronized更加的靈活。
2、使用ReentranLock實現同步
既然ReentrantLock類在功能上相比synchronized更多,那麼就以一個初步的程式示例來介紹一下ReentrantLock類的使用。
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class MyService{ private Lock lock=new ReentrantLock(); public void testMethod() { lock.lock(); for(int i=0;i<5;i++) { System.out.println("ThreadName= "+Thread.currentThread().getName()+(" "+(i+1))); } lock.unlock(); } } class MyThread extends Thread{ private MyService service; public MyThread(MyService service) { this.service=service; } @Override public void run() { service.testMethod(); } } public class LockTest { public static void main(String[] args) { MyService service=new MyService(); MyThread t1=new MyThread(service); MyThread t2=new MyThread(service); MyThread t3=new MyThread(service); MyThread t4=new MyThread(service); MyThread t5=new MyThread(service); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
執行結果:
ThreadName= Thread-2 1 ThreadName= Thread-2 2 ThreadName= Thread-2 3 ThreadName= Thread-2 4 ThreadName= Thread-2 5 ThreadName= Thread-0 1 ThreadName= Thread-0 2 ThreadName= Thread-0 3 ThreadName= Thread-0 4 ThreadName= Thread-0 5 ThreadName= Thread-3 1 ThreadName= Thread-3 2 ThreadName= Thread-3 3 ThreadName= Thread-3 4 ThreadName= Thread-3 5 ThreadName= Thread-4 1 ThreadName= Thread-4 2 ThreadName= Thread-4 3 ThreadName= Thread-4 4 ThreadName= Thread-4 5 ThreadName= Thread-1 1 ThreadName= Thread-1 2 ThreadName= Thread-1 3 ThreadName= Thread-1 4 ThreadName= Thread-1 5
從執行的結果來看,當前執行緒列印完畢之後將鎖進行釋放,其他執行緒才可以繼續列印。執行緒列印的資料是分組列印,因為當前執行緒已經持有鎖,但執行緒之間列印的順序是隨機的。lock.lock()是對當前執行緒加鎖,當執行緒執行完畢後呼叫lock.unlock()釋放鎖,這時候其他執行緒可以去獲取鎖,至於是哪一個執行緒可以爭搶到鎖還是看CPU的排程
3、使用Condition實現等待/通知:錯誤用法與解決
關鍵字synchronized與wait()和notify()/notifyAll()方法相結合可以實現等待/通知模式,類ReentrantLock也可以實現同樣的功能,但需要藉助於Condition物件。Condition類是在JDK5中出現的技術,使用它有更好的靈活性,比如可以實現多路通知功能,也就是在一個Lock物件裡面可以建立多個Condition(即物件監視器)例項,執行緒物件可以註冊在指定的Condition中,從而可以有選擇性地進行執行緒通知,在排程執行緒上更加靈活。
在使用notify(O/notifyAll0方法進行通知時,被通知的執行緒卻是由JVM隨機選擇的。但使用ReentrantLock結合Condition類是可以實現前面介紹過的“選擇性通知”,這個功能是非常重要的,而且在Condition類中是預設提供的。
而synchronized就相當於整個Lock物件中只有一個單一的Condition物件,所有的執行緒都註冊在它一個物件的身上。執行緒開始notifyAll()時,需要通知所有的WAITING執行緒,沒有選擇權,會出現相當大的效率問題。
package Thread05; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class MyService{ private Lock lock=new ReentrantLock(); private Condition condition=lock.newCondition(); public void await() { try { lock.lock(); System.out.println("A"); condition.await(); System.out.println("B"); }catch(InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); System.out.println("鎖釋放了"); } } } class MyThread extends Thread{ private MyService service; public MyThread(MyService service) { this.service=service; } @Override public void run() { service.await(); } } public class LockTest { public static void main(String[] args) { MyService service=new MyService(); MyThread thread=new MyThread(service); thread.start(); } }
輸出結果:
A
我們可以看到輸出結果只有一個A,並沒有其他的輸出,這是因為呼叫Condition的await()方法,使當前執行任務的執行緒進入了等待的狀態
注意:在使用Condition方法時要先呼叫lock.lock()程式碼獲得同步監視器
4、正確使用Condition實現等待/通知
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class MyService{ private Lock lock=new ReentrantLock(); private Condition condition=lock.newCondition(); public void await() { try { lock.lock(); System.out.println("await時間為"+System.currentTimeMillis()); condition.await(); }catch(InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); System.out.println("鎖釋放了"); } } public void signal() { try { lock.lock(); System.out.println("signal時間為"+System.currentTimeMillis()); condition.signal(); }finally { lock.unlock(); } } } class MyThread extends Thread{ private MyService service; public MyThread(MyService service) { this.service=service; } @Override public void run() { service.await(); } } public class LockTest { public static void main(String[] args) throws InterruptedException { MyService service=new MyService(); MyThread thread=new MyThread(service); thread.start(); Thread.sleep(3000); service.signal(); } }
執行結果:
await時間為1575599786039 signal時間為1575599789051 鎖釋放了
成功實現等待/通知模式
Object類中的wait()方法相當於Condition類中的await()方法,Object類中的wait(long timeout)方法相當於Condition類中的await(long time,TimeUnit unit)方法。Object類中的notify()方法相當於Condition類中的signal()方法。Object類中的notifyAll()方法相當於Condition類中的signalAll()方法。
5、使用多個Condition實現通知所有執行緒
前面使用一個Condition物件來實現等待/通知模式,其實Condition物件也可以建立多個。那麼一個Condition物件和多個Condition物件在使用上有什麼區別呢?
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class MyService{ private Lock lock=new ReentrantLock(); private Condition condition=lock.newCondition(); public void awaitA() { try { lock.lock(); System.out.println("begin awaitA時間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName()); condition.await(); System.out.println("end awaitA時間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName()); }catch(InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public void awaitB() { try { lock.lock(); System.out.println("begin awaitB時間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName()); condition.await(); System.out.println("end awaitB時間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName()); }catch(InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public void signalAll() { try { lock.lock(); System.out.println("signalAll時間為"+System.currentTimeMillis()); condition.signalAll(); }finally { lock.unlock(); } } } class MyThreadA extends Thread{ private MyService service; public MyThreadA(MyService service) { this.service=service; } @Override public void run() { service.awaitA(); } } class MyThreadB extends Thread{ private MyService service; public MyThreadB(MyService service) { this.service=service; } @Override public void run() { service.awaitB(); } } public class LockTest { public static void main(String[] args) throws InterruptedException { MyService service=new MyService(); MyThreadA threadA=new MyThreadA(service); threadA.setName("A"); threadA.start(); MyThreadB threadB=new MyThreadB(service); threadB.setName("B"); threadB.start(); Thread.sleep(3000); service.signalAll(); } }
執行結果:
begin awaitA時間為1575600904529ThreadNameA begin awaitB時間為1575600904545ThreadNameB signalAll時間為1575600907537 end awaitA時間為1575600907537ThreadNameA end awaitB時間為1575600907537ThreadNameB
6、使用多個Condition實現通知部分執行緒
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class MyService{ private Lock lock=new ReentrantLock(); private Condition conditionA=lock.newCondition(); private Condition conditionB=lock.newCondition(); public void awaitA() { try { lock.lock(); System.out.println("begin awaitA時間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName()); conditionA.await(); System.out.println("end awaitA時間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName()); }catch(InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public void awaitB() { try { lock.lock(); System.out.println("begin awaitB時間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName()); conditionB.await(); System.out.println("end awaitB時間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName()); }catch(InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } //通知A public void signalAll_A() { try { lock.lock(); System.out.println("signalAll_A時間為"+System.currentTimeMillis()+"ThreadName="+Thread.currentThread().getName()); conditionA.signalAll(); }finally { lock.unlock(); } } //通知B public void signalAll_B() { try { lock.lock(); System.out.println("signalAll_A時間為"+System.currentTimeMillis()+"ThreadName="+Thread.currentThread().getName()); conditionA.signalAll(); }finally { lock.unlock(); } } } class MyThreadA extends Thread{ private MyService service; public MyThreadA(MyService service) { this.service=service; } @Override public void run() { service.awaitA(); } } class MyThreadB extends Thread{ private MyService service; public MyThreadB(MyService service) { this.service=service; } @Override public void run() { service.awaitB(); } } public class LockTest { public static void main(String[] args) throws InterruptedException { MyService service=new MyService(); MyThreadA threadA=new MyThreadA(service); threadA.setName("A"); threadA.start(); MyThreadB threadB=new MyThreadB(service); threadB.setName("B"); threadB.start(); Thread.sleep(3000); service.signalAll_A(); } }
執行結果:
begin awaitA時間為1575601785167ThreadNameA begin awaitB時間為1575601785167ThreadNameB signalAll_A時間為1575601788181ThreadName=main end awaitA時間為1575601788181ThreadNameA
上面的程式碼實現通知部分執行緒,定義了兩個Condition,在測試類中只是喚醒了A,從輸出結果可以看出,執行緒A被喚醒了,執行緒B依然處於等待狀態
7、實現生產者/消費者模式:一個生產者一個消費者
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class MyService{ private Lock lock=new ReentrantLock(); private Condition condition=lock.newCondition(); private boolean hasValue=false; public void set() { try { lock.lock(); while(hasValue==true) { condition.await(); } System.out.println("列印★"); hasValue=true; condition.signal(); }catch(Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void get() { try { lock.lock(); while(hasValue==false) { condition.await(); } System.out.println("列印☆"); hasValue=false; condition.signal(); }catch(Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } } class MyThreadA extends Thread{ private MyService service; public MyThreadA(MyService service) { this.service=service; } @Override public void run() { for(int i=0;i<Integer.MAX_VALUE;i++) { service.set(); } } } class MyThreadB extends Thread{ private MyService service; public MyThreadB(MyService service) { this.service=service; } @Override public void run() { for(int i=0;i<Integer.MAX_VALUE;i++) { service.get(); } } } public class LockTest { public static void main(String[] args) throws InterruptedException { MyService service=new MyService(); MyThreadA a=new MyThreadA(service); a.start(); MyThreadB b=new MyThreadB(service); b.start(); } }
執行結果:
上面程式碼實現了生產者消費者的功能,一個生產一個消費,如果hasValue=false相當於生產者沒有生產產品,當前沒有可消費的產品,所以呼叫生產者生產,當hasValue=true說明當前有產品還沒有被消費,那麼生產者應該停止生產,呼叫消費者消費
8、實現生產者/消費者模式:多個生產者多個消費者
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class MyService{ private Lock lock=new ReentrantLock(); private Condition condition=lock.newCondition(); private boolean hasValue=false; public void set() { try { lock.lock(); while(hasValue==true) { System.out.println("有可能★★連續"); condition.await(); } System.out.println("列印★"); hasValue=true; condition.signal(); }catch(Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void get() { try { lock.lock(); while(hasValue==false) { System.out.println("有可能☆☆連續"); condition.await(); } System.out.println("列印☆"); hasValue=false; condition.signal(); }catch(Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } } class MyThreadA extends Thread{ private MyService service; public MyThreadA(MyService service) { this.service=service; } @Override public void run() { for(int i=0;i<Integer.MAX_VALUE;i++) { service.set(); } } } class MyThreadB extends Thread{ private MyService service; public MyThreadB(MyService service) { this.service=service; } @Override public void run() { for(int i=0;i<Integer.MAX_VALUE;i++) { service.get(); } } } public class LockTest { public static void main(String[] args) throws InterruptedException { MyService service=new MyService(); MyThreadA[] threadA=new MyThreadA[10]; MyThreadB[] threadB=new MyThreadB[10]; for(int i=0;i<10;i++) { threadA[i]=new MyThreadA(service); threadB[i]=new MyThreadB(service); threadA[i].start(); threadB[i].start(); } } }
執行結果:
執行程式後出現了假死,因為出現了生產者釋放生產者或者消費者釋放消費者的情況,那麼該如何解決這個問題呢?在使用synchronized實現生產者消費者的時候我們也遇到過這種情況,當時是使用notifyAll()來解決這個問題的,那麼現在使用鎖我們則用signalAll()方法來解決死鎖問題,將上述程式碼中signal()方法改成signalAll()即可
修改後程式執行結果如下
程式一直正常執行,沒有出現死鎖情況
9、公平鎖和非公平鎖
公平與非公平鎖:鎖Lock分為“公平鎖”和“非公平鎖”,公平鎖表示執行緒獲取鎖的順序是按照執行緒加鎖的順序來分配的,即先來先得的FIFO先進先出順序。而非公平鎖就是一種獲取鎖的搶佔機制,是隨機獲得鎖的,和公平鎖不一樣的就是先來的不一定先得到鎖,這個方式可能造成某些執行緒一直拿不到鎖,結果也就是不公平的了。
建立公平鎖和非公平鎖ReentrantLock lock=new ReentrantLock(boolean a),建立鎖時如果a為true的話,則建立的是公平鎖,如果a為false的話,則建立的是非公平鎖
公平鎖
import java.util.concurrent.locks.ReentrantLock; class Service{ private ReentrantLock lock; public Service(boolean isFair) { lock=new ReentrantLock(isFair); } public void serviceMethod() { try { lock.lock(); System.out.println("ThreadName="+Thread.currentThread().getName()+"獲得鎖定"); }catch(Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } } public class LockTest { public static void main(String[] args) throws InterruptedException { final Service service=new Service(true); Runnable runnable=new Runnable() { @Override public void run() { System.out.println("★執行緒"+Thread.currentThread().getName()+"運行了"); service.serviceMethod(); } }; Thread[] threadArray=new Thread[10]; for(int i=0;i<10;i++) { threadArray[i]=new Thread(runnable); } for(int i=0;i<10;i++) { threadArray[i].start(); } } }
執行結果:
★執行緒Thread-2運行了 ★執行緒Thread-3運行了 ★執行緒Thread-0運行了 ★執行緒Thread-9運行了 ★執行緒Thread-4運行了 ★執行緒Thread-8運行了 ★執行緒Thread-5運行了 ★執行緒Thread-1運行了 ★執行緒Thread-6運行了 ★執行緒Thread-7運行了 ThreadName=Thread-2獲得鎖定 ThreadName=Thread-6獲得鎖定 ThreadName=Thread-1獲得鎖定 ThreadName=Thread-8獲得鎖定 ThreadName=Thread-0獲得鎖定 ThreadName=Thread-7獲得鎖定 ThreadName=Thread-5獲得鎖定 ThreadName=Thread-3獲得鎖定 ThreadName=Thread-9獲得鎖定 ThreadName=Thread-4獲得鎖定
結果顯示輸出基本是呈有序的狀態,這就是公平鎖的特點
非公平鎖
import java.util.concurrent.locks.ReentrantLock; class Service{ private ReentrantLock lock; public Service(boolean isFair) { lock=new ReentrantLock(isFair); } public void serviceMethod() { try { lock.lock(); System.out.println("ThreadName="+Thread.currentThread().getName()+"獲得鎖定"); }catch(Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } } public class LockTest { public static void main(String[] args) throws InterruptedException { final Service service=new Service(false); Runnable runnable=new Runnable() { @Override public void run() { System.out.println("★執行緒"+Thread.currentThread().getName()+"運行了"); service.serviceMethod(); } }; Thread[] threadArray=new Thread[10]; for(int i=0;i<10;i++) { threadArray[i]=new Thread(runnable); } for(int i=0;i<10;i++) { threadArray[i].start(); } } }
執行結果:
★執行緒Thread-2運行了 ★執行緒Thread-9運行了 ★執行緒Thread-7運行了 ★執行緒Thread-0運行了 ★執行緒Thread-3運行了 ★執行緒Thread-1運行了 ★執行緒Thread-6運行了 ★執行緒Thread-5運行了 ★執行緒Thread-4運行了 ThreadName=Thread-1獲得鎖定 ★執行緒Thread-8運行了 ThreadName=Thread-8獲得鎖定 ThreadName=Thread-2獲得鎖定 ThreadName=Thread-7獲得鎖定 ThreadName=Thread-5獲得鎖定 ThreadName=Thread-3獲得鎖定 ThreadName=Thread-4獲得鎖定 ThreadName=Thread-9獲得鎖定 ThreadName=Thread-0獲得鎖定 ThreadName=Thread-6獲得鎖定
非公平鎖的執行結果基本上是亂序的,說明先start()啟動的執行緒不代表先獲得鎖
10、ReentranLock方法概述:
(1)、int getHoldCount()
getHoldCount()的作用是查詢當前執行緒保持此鎖定的個數,也就是呼叫lock()方法的次數。
(2)、int getQueueLength()
getQueueLength()的作用是返回正等待獲取此鎖定的執行緒估計數,比如有5個執行緒,1個執行緒首先執行awai()方法,那麼在呼叫getQueueLength()方法後返回值是4,說明有4個執行緒同時在等待lock的釋放。
(3)、int getWaitQueueLength(Condition condition)
getWaitQueueLength(Condition condition)的作用是返回等待與此鎖定相關的給定條件Condition的執行緒估計數,比如有5個執行緒,每個執行緒都執行了同一個condition物件的await()方法,則呼叫getWaitQueueLength(Condition condition)方法時返回的int值是5。
(4)、boolean hasQueuedThread(Thread thread)
hasQueuedThread(Thread thread)的作用是查詢指定的執行緒是否正在等待獲取此鎖定
hasQueuedThreads()的作用是查詢是否有執行緒正在等待獲取此鎖定。
(5)、boolean hasWaiters(Condition condition)
hasWaiters(Condition condition)的作用是查詢是否有執行緒正在等待與此鎖定有關的condition條件。
(6)、boolean isFair()
isFair()的作用是判斷是不是公平鎖
(7)、boolean isHeldByCurrentThread()
isHeldByCurrentThread的作用是查詢當前執行緒是否保持此鎖定
(8)、boolean isLocked()
isLocked()的作用是查詢此鎖定是否由任意的執行緒保持
ReentrantReadWriteLock
類ReentrantLock具有完全互斥排他的效果,即同一時間只有一個執行緒在執行ReentrantLock.lock()方法後面的任務。這樣做雖然保證了例項變數的執行緒安全性,但效率卻是非常低下的。所以在JDK中提供了一種讀寫鎖ReentrantReadWriteLock類,使用它可以加快執行效率,在某些不需要操作例項變數的方法中,完全可以使用讀寫鎖ReentrantReadWriteLock 來提升該方法的程式碼執行速度。
讀寫鎖表示也有兩個鎖,一個是讀操作相關的鎖,也稱為共享鎖;另一個是寫操作相關的鎖,也叫排他鎖。也就是多個讀鎖之間不互斥,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥。在沒有執行緒Thread進行寫入操作時,進行讀取操作的多個Thread都可以獲取讀鎖,而進行寫入操作的Thread只有在獲取寫鎖後才能進行寫入操作。即多個Thread可以同時進行讀取操作,但是同一時刻只允許一個Thread進行寫入操作。
一、ReentrantReadWriteLock讀讀共享
import java.util.concurrent.locks.ReentrantReadWriteLock; class Service{ private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public void read() { try { try { lock.readLock().lock(); System.out.println("獲取讀鎖"+Thread.currentThread().getName()+" "+System.currentTimeMillis()); Thread.sleep(10000); }finally { lock.readLock().unlock(); } }catch(Exception e) { e.printStackTrace(); } } } class MyThreadA extends Thread{ private Service service; public MyThreadA(Service service) { this.service=service; } @Override public void run() { service.read(); } } class MyThreadB extends Thread{ private Service service; public MyThreadB(Service service) { this.service=service; } @Override public void run() { service.read(); } } public class LockTest { public static void main(String[] args) throws InterruptedException { Service service=new Service(); MyThreadA a=new MyThreadA(service); a.setName("A"); MyThreadB b=new MyThreadB(service); b.setName("B"); a.start(); b.start(); } }
執行結果:
獲取讀鎖A 1575611161158 獲取讀鎖B 1575611161158
從輸出結果列印的時間來看,兩個執行緒幾乎同時進入lock()方法後面的程式碼。說明在此使用了lock.readLock()讀鎖可以提高程式執行效率,允許多個執行緒同時執行lock()方法後面的程式碼。
二、ReentrantReadWriteLock寫寫互斥
import java.util.concurrent.locks.ReentrantReadWriteLock; class Service{ private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public void write() { try { try { lock.writeLock().lock(); System.out.println("獲取寫鎖"+Thread.currentThread().getName()+" "+System.currentTimeMillis()); Thread.sleep(10000); }finally { lock.writeLock().unlock(); } }catch(Exception e) { e.printStackTrace(); } } } class MyThreadA extends Thread{ private Service service; public MyThreadA(Service service) { this.service=service; } @Override public void run() { service.write(); } } class MyThreadB extends Thread{ private Service service; public MyThreadB(Service service) { this.service=service; } @Override public void run() { service.write(); } } public class LockTest { public static void main(String[] args) throws InterruptedException { Service service=new Service(); MyThreadA a=new MyThreadA(service); a.setName("A"); MyThreadB b=new MyThreadB(service); b.setName("B"); a.start(); b.start(); } }
執行結果:
獲取寫鎖B 1575611458260 獲取寫鎖A 1575611468273
結果顯示寫鎖的效果是同一時間只允許一個執行緒執行lock()後面的程式碼
三、ReentrantReadWriteLock讀寫互斥
import java.util.concurrent.locks.ReentrantReadWriteLock; class Service{ private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public void read() { try { try { lock.readLock().lock(); System.out.println("獲取讀鎖"+Thread.currentThread().getName()+" "+System.currentTimeMillis()); Thread.sleep(10000); }finally { lock.readLock().unlock(); } }catch(Exception e) { e.printStackTrace(); } } public void write() { try { try { lock.writeLock().lock(); System.out.println("獲取寫鎖"+Thread.currentThread().getName()+" "+System.currentTimeMillis()); Thread.sleep(10000); }finally { lock.writeLock().unlock(); } }catch(Exception e) { e.printStackTrace(); } } } class MyThreadA extends Thread{ private Service service; public MyThreadA(Service service) { this.service=service; } @Override public void run() { service.read(); } } class MyThreadB extends Thread{ private Service service; public MyThreadB(Service service) { this.service=service; } @Override public void run() { service.write(); } } public class LockTest { public static void main(String[] args) throws InterruptedException { Service service=new Service(); MyThreadA a=new MyThreadA(service); a.setName("A"); MyThreadB b=new MyThreadB(service); b.setName("B"); a.start(); b.start(); } }
執行結果:
獲取讀鎖A 1575611689661 獲取寫鎖B 1575611699665
從讀寫的時間上可以看出讀寫的操作時互斥的
&n