Java——多線程之Lock鎖


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

  

從讀寫的時間上可以看出讀寫的操作時互斥的

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM