Java併發編程十 重入鎖ReentrantLock 讀寫鎖ReentrantReadWriteLock
Java的concurrent併發包提供了一套與Synchronized關鍵字相同功能Lock,還有與wait()/notify()相同功能的Condition。
Lock的功能比傳統的Synchronized更加靈活。它具有嗅探鎖定和多路分支等功能(一個lock對象可以創建多個Condition)。
1.Lock和synchronized的區別和各自的特點
1、類型不同
Lock是一個接口,是JDK層面的實現;synchronized是Java的關鍵字,是JVM層面的實現,是Java的內置特性;這也造成了在鎖管理上的不同。
2、釋放鎖的方式
Lock是在編碼中進行鎖的操作,需要主動調用接口中的方法釋放鎖,所以使用Lock時需要配合try模塊,在finally中釋放鎖,不然一旦發生異常很可能造成死鎖現象;synchronized由於是JVM層面的實現,獲取鎖和釋放鎖的操作是不可見的,當發生異常時,鎖的釋放由JVM實現。
3、獲取鎖的過程
Lock接口中提供的方法,讓獲取鎖的過程更加靈活,編程人員可以方便的在獲取鎖的過程中進行多種操作,比如嘗試獲取鎖、設置獲取鎖的超時時間、中斷等待獲取鎖的線程等,應該說讓鎖的操作變得“豐富多彩”起來;synchronized是無法這麼靈活的對鎖進行操作的。
4、效率
基於讀寫鎖接口的擴展,Lock可以提高多個線程進行讀操作的效率,在大併發量的情況下,效率的提高會尤其明顯。
2.ReentrantLock
ReentrantLock 重入鎖 它與Synchronized關鍵字的功能相同只是需要自己手動釋放鎖,而線程間的通信機制wait()/notify()也有相應的代替實現Condition。其原理和Synchronized是一樣的。
/**
* Created by lyyz on 18-5-17.
*/
public class ReentrantLock_eg {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition1 = reentrantLock.newCondition();
Condition condition2 = reentrantLock.newCondition();
public void method1(){
try {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"進入mehtod1");
System.out.println(Thread.currentThread().getName()+"condition1 休眠");
condition1.await();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"完成mehtod1");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public void method2(){
try {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"進入mehtod2");
System.out.println(Thread.currentThread().getName()+"condition2 休眠");
condition2.await();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"完成mehtod2");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public void method3(){
try {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"進入mehtod3");
System.out.println(Thread.currentThread().getName()+"喚醒condition1 休眠狀態的線程 ");
condition1.signal();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"完成mehtod3");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public void method4(){
try {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"進入mehtod4");
System.out.println(Thread.currentThread().getName()+"休眠");
System.out.println(Thread.currentThread().getName()+"喚醒condition2 休眠狀態的線程 ");
condition2.signal();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"完成mehtod4");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public static void main(String[] args) {
final ReentrantLock_eg reentrantLock_eg = new ReentrantLock_eg();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
reentrantLock_eg.method1();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
reentrantLock_eg.method2();
}
},"t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
reentrantLock_eg.method3();
}
},"t3");
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
reentrantLock_eg.method4();
}
},"t4");
t1.start();
t2.start();
t3.start();
t4.start();
// run output
// t1進入mehtod1
// t1condition1 休眠
// t2進入mehtod2
// t2condition2 休眠
// t3進入mehtod3
// t3喚醒condition1 休眠狀態的線程
// t3完成mehtod3
// t1完成mehtod1
// t4進入mehtod4
// t4休眠
// t4喚醒condition2 休眠狀態的線程
// t4完成mehtod4
// t2完成mehtod2
}
}
3.ReentrantReadWriteLock
ReentrantReadWriteLock 讀寫鎖,多用於讀多寫少的情況。它的特點就是 讀讀共享 寫寫互斥,讀寫互斥
/**
* Created by lyyz on 18-5-17.
*/
public class ReentrantReadWriteLock_eg {
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
public void read(){
try {
readLock.lock();
System.out.println("當前線程:" + Thread.currentThread().getName() + "進入...");
Thread.sleep(3000);
System.out.println("當前線程:" + Thread.currentThread().getName() + "退出...");
} catch (Exception e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
}
public void write(){
try {
writeLock.lock();
System.out.println("當前線程:" + Thread.currentThread().getName() + "進入...");
Thread.sleep(3000);
System.out.println("當前線程:" + Thread.currentThread().getName() + "退出...");
} catch (Exception e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
public static void main(String[] args) {
ReentrantReadWriteLock_eg reentrantReadWriteLock_eg = new ReentrantReadWriteLock_eg();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
reentrantReadWriteLock_eg.read();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
reentrantReadWriteLock_eg.read();
}
},"t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
reentrantReadWriteLock_eg.write();
}
},"t3");
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
reentrantReadWriteLock_eg.write();
}
},"t4");
//讀讀共享,兩個線程可同時進行讀取
// t1.start();
// t2.start();
// 當前線程:t2進入...
// 當前線程:t1進入...
// 當前線程:t2退出...
// 當前線程:t1退出...
//寫寫互斥 一個線程執行完以後另一個線程才能執行
// t3.start();
// t4.start();
// 當前線程:t3進入...
// 當前線程:t3退出...
// 當前線程:t4進入...
// 當前線程:t4退出...
//讀寫互斥
// t1.start();
// t3.start();
// 當前線程:t1進入...
// 當前線程:t1退出...
// 當前線程:t3進入...
// 當前線程:t3退出...
}
}