ReentrantLock具有完全互斥排他的效果,也即同一時間只有一個執行緒在執行ReentrantLock.lock()方法後邊的任務。這樣做保證了例項變數的執行緒安全性,但是效率低下。
ReentrantReadWriteLock類是ReadWriteLock介面的實現類,這是一個讀寫鎖。讀寫鎖維護了兩個鎖,一個是讀相關的,也稱之為共享鎖;另外一個是寫相關的,也叫排他鎖。多個讀鎖之間不排斥,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥。具體而言,就是多個執行緒可以同時進行讀取操作,但是同一時刻只允許一個執行緒進行寫入操作。
ReentarntReadWriteLock特性
- 公平性:預設是公平鎖,同時支援非公平鎖,吞吐量上來看是非公平優於公平
- 重入:支援重進入。讀執行緒在獲取讀鎖後,能再次獲取讀鎖。寫執行緒獲取了寫鎖後能再次獲取寫鎖,也能同時獲取讀鎖。
- 鎖降級:遵循獲取寫鎖、獲取讀鎖再釋放寫鎖的次序,寫鎖能降級成為讀鎖
ReentarntReadWriteLock的使用
讀讀共享示例程式碼:
class Service{
private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
public void read(){
try {
lock.readLock().lock();
System.out.println("獲得讀鎖 "+Thread.currentThread().getName()+" "+new Date(System.currentTimeMillis()));
Thread.sleep(50000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
}
class MyThread extends Thread{
private Service service;
public MyThread(Service service){
this.service=service;
}
@Override
public void run() {
service.read();
}
}
public class Run {
public static void main(String[] args) {
Service service=new Service();
MyThread a=new MyThread(service);
a.setName("A");
MyThread b=new MyThread(service);
b.setName("B");
a.start();
b.start();
}
}
複製程式碼
執行結果:
獲得讀鎖 B Thu Dec 13 16:09:29 CST 2018
獲得讀鎖 A Thu Dec 13 16:09:29 CST 2018
複製程式碼
可以發現,二者是近乎同時進行讀取的,也就是讀讀是共享的。也就是說允許了多個執行緒同時執行lock()方法後面的程式碼。
對於寫存在的情況,修改程式碼如下:
class Service{
private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
public void read(){
try {
lock.readLock().lock();
System.out.println("獲得讀鎖 "+Thread.currentThread().getName()+" "+new Date(System.currentTimeMillis()));
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
public void write(){
try {
lock.writeLock().lock();
System.out.println("獲得寫鎖 "+Thread.currentThread().getName()+" "+new Date(System.currentTimeMillis()));
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
}
class MyReadThread extends Thread{
private Service service;
public MyReadThread(Service service){
this.service=service;
}
@Override
public void run() {
service.read();
}
}
class MyWriteThread extends Thread{
private Service service;
public MyWriteThread(Service service){
this.service=service;
}
@Override
public void run() {
service.write();
}
}
public class Run {
public static void main(String[] args) {
Service service=new Service();
MyReadThread read1=new MyReadThread(service);
read1.setName("read1");
MyReadThread read2=new MyReadThread(service);
read2.setName("read2");
MyWriteThread write1=new MyWriteThread(service);
write1.setName("write1");
MyWriteThread write2=new MyWriteThread(service);
write2.setName("write2");
read1.start();
write1.start();
write2.start();
read2.start();
}
}
複製程式碼
執行結果:
獲得寫鎖 write1 Thu Dec 13 16:16:24 CST 2018
獲得讀鎖 read1 Thu Dec 13 16:16:29 CST 2018
獲得寫鎖 write2 Thu Dec 13 16:16:34 CST 2018
獲得讀鎖 read2 Thu Dec 13 16:16:39 CST 2018
複製程式碼
可以看到,四個執行緒是互斥的執行的,每個執行緒都是等待了5秒才執行。也就是說,只要出現寫操作的過程就是互斥的。
參考資料
- 高洪巖. Java多執行緒程式設計核心技術[M]. 機械工業出版社, 2015
- blog.csdn.net/qq_34337272…