1.可重入鎖ReentrantLock,相當於synchronized塊,爲臨界區提供互斥訪問機制.
(1).相關的接口
創建一個可重入鎖
Lock lock = new ReentrantLock();
請求鎖,如果鎖被當前另一個線程持有,則阻塞。
void lock()
釋放鎖
void unlock();
非阻塞型lock()
boolean tryLock();
(2).使用基本結構
locker.lock();
try{
//code here to access the cirtical section
}finally{
locker.unlock();
}
這種結構保證在任何時刻只有一個線程能夠進入臨界區,如果一個線程鎖住了鎖對象,其他任何線程在調用lock時,都會被阻塞,直到第一個線程釋放鎖對象。而且無論try塊是否拋出異常,都會執行finally block,解鎖locker.
(3).鎖的可重入性:鎖是可重入的,線程能夠重複地獲取它已經擁有的鎖。鎖對象維護一個持有計數(hold count)來追蹤對lock方法的嵌套調用。線程在每次調用lock後都要調用unlock來釋放鎖。由於這個特性,被一個鎖保護的代碼可以調用另一個使用相同鎖的方法。
(4).示例代碼:
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- class WorkerOne extends Thread{
- private Lock locker;
- public WorkerOne (Lock locker){
- this.locker = locker;
- }
- public void run(){
- locker.lock();
- try{
- System.out.println(Thread.currentThread().getName()+":step into critical section");
- }finally{
- locker.unlock();
- }
- }
- }
- class WorkerTwo extends Thread{
- private Lock locker;
- public WorkerTwo (Lock locker){
- this.locker = locker;
- }
- public void sayHello(){
- locker.lock();
- try{
- System.out.println(Thread.currentThread().getName()+":call sayHello()");
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally{
- locker.unlock();
- }
- }
- public void run(){
- locker.lock();
- try{
- System.out.println(Thread.currentThread().getName()+":setp into critical section");
- //測試鎖的可重入性
- sayHello();
- }finally{
- locker.unlock();
- }
- }
- }
- public class Test5 {
- public static void main(String[] args) {
- Lock locker = new ReentrantLock();
- WorkerOne wo= new WorkerOne(locker);
- wo.setName("WorkerOne");
- WorkerTwo wt = new WorkerTwo(locker);
- wt.setName("WorkerTwo");
- wt.start();
- wo.start();
- }
- }
輸出:
WorkerTwo:setp into critical section
WorkerTwo:call sayHello()
WorkerOne:step into critical section
2.條件對象Condition,相當於wait-notify機制,提供一種線程間的等待通知機制,condition中的等待-通知方法是await(),signal
(),signalAll(),也需要在互斥環境下被調用。
(1)相關的接口
創建Condition對象,Condition對象是跟Lock關聯在一起的.
Lock locker = new ReentrantLock();
Condition cond = locker.newCondition();
把此線程放到條件的等待集中。
void await();
解除此條件的等待集中所有線程的阻塞狀態
void signalAll();
在此條件的等待集中隨機選擇一個線程,解除其阻塞狀態。
void signal();
(2).使用的基本結構
- //初始時ok_to_proceed爲false.
- locker.lock()
- try{
- while(!ok_to_proceed){
- //進入等待此條件集中,被阻塞,它維持狀態直到另一個線程調用同一個條件上的
- //signalAll/signal方法時爲止。
- cond.await();
- }
- }finally{
- cker.unlock();
- }
- locker.lock();
- try{
- //調用將解除所有等待此條件下的線程的阻塞狀態。當線程從等待集中被移走時,它們將再次成爲可運行的,調度器將再次激活它們
- //此時,它們將試圖重新進入對象。一旦鎖可獲得,它們中的某個線程將從await調用返回,從而獲得鎖並從它被阻塞的地方繼續執行。
- ok_to_proceed = true;
- cond.signalAll() or cond.signal();
- }finally{
- locker.unlock();
- }
ok_to_proceed也是爲了防止wait-notify出現的問題,即再wait之間,notify()已經給出通知,此時wait只會一直等待下去,這樣就保證了signal()線程的通知被await()線程接收到。
(3)測試代碼:
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- class GlobalV{
- public final static Lock locker = new ReentrantLock();
- public final static Condition cond = locker.newCondition();
- public static boolean to_proceed = false;
- }
- class Response extends Thread{
- public void run(){
- while(true){
- GlobalV.locker.lock();
- try{
- while(!GlobalV.to_proceed){
- GlobalV.cond.await();
- }
- System.out.println("Response:finish a job");
- GlobalV.to_proceed = false;
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- GlobalV.locker.unlock();
- }
- }
- }
- }
- class Request extends Thread{
- public void run(){
- while(true){
- GlobalV.locker.lock();
- try{
- GlobalV.to_proceed = true;
- GlobalV.cond.signalAll();
- System.out.println("Request:send a job to Response");
- }finally{
- GlobalV.locker.unlock();
- }
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public class Test6 {
- public static void main(String[] args) {
- Request req = new Request();
- Response res = new Response();
- req.start();
- res.start();
- }
- }
輸出:
Request:send a job to Response
Response:finish a job
Request:send a job to Response
Response:finish a job
Request:send a job to Response
Response:finish a job
Request:send a job to Response
Response:finish a job
3.讀寫鎖ReentrantReadWriteLock,適用於"讀多寫少"的多線程應用場景,"讀-寫"互斥,"寫-寫"互斥,而讀-讀可以共享同讀鎖,即一個線程獲取讀鎖,其它線程可直接進入讀,不會被阻塞。
(1).相關接口
創建讀寫鎖對象
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
獲取讀鎖
Lock readLock = rwLock.readLock();
獲取寫鎖
Lock writeLock = rwLock.writeLock();
(2).讀寫鎖使用基本結構
//對所有的讀操作添加讀鎖
- readLock.lock();
- try{
- //code to read
- }finally{
- readLock.unlock();
- }
//對所有的寫操作添加寫鎖
- writeLock.lock();
- try{
- //code to write
- }finally{
- writeLock.unlock();
- }
(3).測試代碼:
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
- class Reader extends Thread {
- private Lock readLock = null;
- public Reader(Lock readLock) {
- this.readLock = readLock;
- }
- public void run() {
- while (true) {
- readLock.lock();
- try {
- System.out.println(Thread.currentThread().getName()
- + ":read action for 1 seconds-"+ReadWriteLock.testVal);
- } finally {
- readLock.unlock();
- }
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- class Writer extends Thread {
- private Lock writeLock = null;
- public Writer(Lock writeLock) {
- this.writeLock = writeLock;
- }
- public void run() {
- while (true) {
- writeLock.lock();
- try {
- System.out.println(Thread.currentThread().getName()
- + ":write action for 2 seconds");
- if(ReadWriteLock.testVal.equals("1111"))
- ReadWriteLock.testVal = "2222";
- else
- ReadWriteLock.testVal = "1111";
- } finally {
- writeLock.unlock();
- }
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public class ReadWriteLock {
- public static String testVal = "Initiation";
- public static void main(String[] args) {
- ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- Lock readLock = lock.readLock();
- Lock writeLock = lock.writeLock();
- Reader reader1 = new Reader(readLock);
- reader1.setName("reader1");
- Reader reader2 = new Reader(readLock);
- reader2.setName("reader2");
- Reader reader3 = new Reader(readLock);
- reader3.setName("reader3");
- Reader reader4 = new Reader(readLock);
- reader4.setName("reader4");
- Writer writer = new Writer(writeLock);
- writer.setName("writer1");
- reader1.start();
- reader2.start();
- reader3.start();
- reader4.start();
- writer.start();
- }
- }
輸出:
reader1:read action for 1 seconds-Initiation
reader3:read action for 1 seconds-Initiation
writer1:write action for 2 seconds
reader2:read action for 1 seconds-1111
reader4:read action for 1 seconds-1111
reader3:read action for 1 seconds-1111
reader1:read action for 1 seconds-1111
reader4:read action for 1 seconds-1111
reader2:read action for 1 seconds-1111
writer1:write action for 2 seconds
reader4:read action for 1 seconds-2222
reader1:read action for 1 seconds-2222
reader3:read action for 1 seconds-2222
reader2:read action for 1 seconds-2222
4.總結
(1).Lock接口替代synchronized
Lock接口可以比sychronized提供更廣泛的鎖定操作.可以提供多把不同的鎖.且鎖之間互不干涉.
Lock接口提供lock()與unlock()方法, 使用明確調用來完成同步的, OO思想好於前者.
Lock可以自由操控同步範圍(scope).
Lock接口支持nested lock(嵌套鎖定).並提供了豐富的api.
Lock接口提供了tryLock()方法, 支持嘗試取得某個object lock.