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
從讀寫的時間上可以看出讀寫的操作時互斥的
總結
以上是生活随笔為你收集整理的Java多线程——锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java第三章 类和对象3.1+3.2
- 下一篇: java第九章IO流与文件操作