java.util.concurrent包中有關於Lock鎖的定義。它提供了ReentrantLock、ReetrantReadWriteLock.ReadLock 和 ReetrantReadWriteLock.WriteLock,重入鎖、讀鎖和寫鎖等。
ReentrantLock
公平鎖和非公平鎖
公平鎖當線程請求鎖時,會將其加入到請求隊列中,
非公平鎖就是當前線程不管有無請求隊列,先去請求鎖,如果請求不到,加入到隊列末尾。
具體分析可以參考
http://www.jianshu.com/p/f7d05d06ef54
默認構造方法創建非公平鎖
public ReentrantLock() {
sync = new NonfairSync();
}
Lock與synchronized的區別
- lock是顯示的鎖,調用lock()添加鎖,必須調用unlock()釋放鎖。
- 使用lock.lock()調用線程的interrupt會中斷線程,而synchronized鎖調用interrupt不會中斷線程。
實例來證明2的結論
我們知道synchronized的interrupt方法只是設置中斷標誌,並沒有真正的中斷線程。下面這個代碼示例用於說明,
代碼如下:
設計兩個線程,一個read線程,一個write線程。
當read線程正在read的時候,write線程處於等待狀態、
然後中斷write線程,看會有什麼情況發生
如果write中斷,會打印 write end…
因爲Reentrant對中斷鎖做出響應,會中斷線程的執行。
public static void syncReaderWriterTest(){
MyReentrantLockBuffer mySyncBuffer = new MyReentrantLockBuffer();
final Thread reader = new Thread(new SyncReader(mySyncBuffer));
final Thread writer = new Thread(new SyncWriter(mySyncBuffer));
reader.start();
writer.start();
System.out.println(Thread.currentThread().getName() + " writer 不在等待了...");
writer.interrupt();
System.out.println(Thread.currentThread().getName() + " writer 已經中斷, " + writer.isInterrupted());
}
/**
* 寫操作,傳入MySyncBuffer的構造方法,保證兩個對象持有相同的對象鎖
*/
static class SyncWriter implements Runnable{
private MyReentrantLockBuffer mySyncBuffer;
public SyncWriter(MyReentrantLockBuffer mySyncBuffer){
this.mySyncBuffer = mySyncBuffer;
}
public void run() {
try {
mySyncBuffer.write();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " write end." + Thread.currentThread().isInterrupted());
}
}
/**
* 讀操作,傳入MySyncBuffer的構造方法,保證兩個對象持有相同的對象鎖
*/
static class SyncReader implements Runnable{
private MyReentrantLockBuffer mySyncBuffer;
public SyncReader(MyReentrantLockBuffer mySyncBuffer){
this.mySyncBuffer = mySyncBuffer;
}
public void run() {
mySyncBuffer.read();
}
}
可重入性
鎖的可重入性體現在兩個方面
1. methodA和methodB擁有同一個對象鎖,線程進入A方法中,在沒有釋放鎖的前提下,可以直接調用methodB方法。
static class ReentrantDemoRunnable implements Runnable{
final ReentrantLock reentrantLock = new ReentrantLock();
public void methodA() {
System.out.println(" lock before methodAAAAAA ========" );
reentrantLock.lock();
try {
System.out.println(" run methodA and lock..." );
this.methodB();
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println(" run methodA end unlock..." );
reentrantLock.unlock();
}
System.out.println(" methodAAAAAA ======== end =====" );
}
public void methodB() {
System.out.println(" lock before methodBBBBBB ========" );
reentrantLock.lock();
try {
System.out.println(" run methodB and lock..." );
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println(" run methodB end unlock..." );
reentrantLock.unlock();
}
System.out.println(" methodBBBBBB ======== end =====" );
}
}
多線程調用methodA執行的順序爲:
lock before methodAAAAAA ========
run methodA and lock...
lock before methodBBBBBB ========
run methodB and lock...
run methodB end unlock...
methodBBBBBB ======== end =====
run methodA end unlock...
methodAAAAAA ======== end =====
- 在加鎖的前提下,在子類方法中調用父類的方法。
static class Father{
final ReentrantLock lock = new ReentrantLock();
public void method(){
lock.lock();
try {
System.out.println("father do something ");
} finally {
lock.unlock();
}
}
}
static class SubClass extends Father{
@Override
public void method(){
lock.lock();
try {
System.out.println("subClass do something ");
super.method();
} finally {
lock.unlock();
}
}
}
public static void reentrantTest2(){
SubClass subClass = new SubClass();
subClass.method();
}
打印結果:
subClass do something
father do something
ReentrantReadWriteLock
讀寫鎖,它有兩個內部類,分別是ReadLock和WriteLock
這兩個鎖分別有以下特性
多個讀鎖不互斥(證明)
一個方法內使用readLock.lock鎖,當多個線程訪問時候,如果線程A持有鎖,線程B也可以持有該鎖。兩個線程無需互相等待。
static class ReadRunnable implements Runnable{
final ReentrantReadWriteLock.ReadLock readLock = new ReentrantReadWriteLock().readLock();
public void run() {
readLock.lock();
System.out.println(Thread.currentThread().getName() + " read start...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + " read end...");
readLock.unlock();
}
}
}
public static void readLockNotMutex(){
ReadRunnable readRunnable = new ReadRunnable();
//創建3個多線程,來說明讀鎖不互斥
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(readRunnable);
thread.start();
}
}
例子說明:創建多個線程,每個線程執行MyRunnable的run方法,run方法中使用讀鎖設置同步。發現會有多個run方法中打印亂序。
打印結果
Thread-0 read start...
Thread-1 read start...
Thread-2 read start...
Thread-0 read end...
Thread-1 read end...
Thread-2 read end...
說明讀鎖不互斥
多個寫鎖互斥(證明)
原理與讀鎖相反
static class WriteRunnable implements Runnable{
final ReentrantReadWriteLock.WriteLock writeLock = new ReentrantReadWriteLock().writeLock();
public void run() {
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " write start...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + " write end...");
writeLock.unlock();
}
}
}
public static void writeLockMutex(){
WriteRunnable writeRunnable = new WriteRunnable();
//創建3個多線程,來說明讀鎖不互斥
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(writeRunnable);
thread.start();
}
}
創建多個線程,每個線程執行MyRunnable的run方法,run方法中使用寫鎖設置同步。發現會有多個run方法中打印不亂序。
打印結果:
Thread-0 write start...
Thread-0 write end...
Thread-1 write start...
Thread-1 write end...
Thread-2 write start...
Thread-2 write end...
說明寫鎖互斥
讀鎖和寫鎖互斥
- 線程A執行writeMethod()方法,獲取到寫鎖,
- 此時線程B執行readMethod()方法,因爲讀寫鎖(寫讀鎖)互斥,所以要等待線程A釋放鎖,然後才能繼續執行。
實例如下:
static class ReadAndWriteLock{
final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
final ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
final ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
public void writeMethod(){
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " 當前爲write Lock========");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + " write lock end ========");
writeLock.unlock();
}
System.out.println(Thread.currentThread().getName() + " 程序結束 end");
}
public void readMethod(){
readLock.lock();
System.out.println(Thread.currentThread().getName() + " 當前爲read Lock========");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + " read lock end ========");
readLock.unlock();
}
System.out.println(Thread.currentThread().getName() + " 程序結束 end");
}
}
public static void readWriteMutex(){
ReadAndWriteLock readAndWriteLock = new ReadAndWriteLock();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
readAndWriteLock.writeMethod();
}
});
t.start();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
readAndWriteLock.readMethod();
}
});
t1.start();
}
打印結果
讀鎖與寫鎖內容分別順序打印。說明讀寫鎖互斥。
Thread-0 當前爲write Lock========
Thread-0 write lock end ========
Thread-0 程序結束 end
Thread-1 當前爲read Lock========
Thread-1 read lock end ========
Thread-1 程序結束 end
讀鎖不支持Condition操作,而寫鎖支持
從源碼中看到,ReadLock的newCondition方法被禁用了
public Condition newCondition() {
throw new UnsupportedOperationException();
}
而WriteLock可以創建
public Condition newCondition() {
return sync.newCondition();
}
支持降級鎖,不支持升級鎖
降級鎖: 線程進入writeLock,沒有釋放鎖,同時獲取到readLock。這時,writeLock會降級爲readLock。但是writeLock的鎖仍需手動釋放。ReentrantReadWriteLock支持降級鎖。
public void downGradeLock(){
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 當前進入寫鎖 write...");
readLock.lock();
System.out.println(Thread.currentThread().getName() + " 當前進入讀鎖 read...");
} finally {
System.out.println(Thread.currentThread().getName() + " 釋放讀鎖...");
readLock.unlock();
System.out.println(Thread.currentThread().getName() + " 釋放寫鎖...");
writeLock.unlock();
}
System.out.println(Thread.currentThread().getName() + " 程序結束...");
}
打印結果:
程序在持有寫鎖的同時,可以獲得讀鎖。說明,ReentrantReadWriteLock支持降級鎖。
main 當前進入寫鎖 write...
main 當前進入讀鎖 read...
main 釋放讀鎖...
main 釋放寫鎖...
main 程序結束...
升級鎖: 線程進入readLock,沒有釋放鎖,同時要獲取writeLock。
這時,程序進入死鎖狀態。ReentrantReadWriteLock不支持升級鎖。
調用下面的方法,會出現死鎖現象。
public void upGradeLock(){
readLock.lock();
System.out.println(Thread.currentThread().getName() + " 當前進入寫鎖 read...");
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " 當前進入寫鎖 write...");
writeLock.unlock();
System.out.println("程序結束....");
}
打印結果:
main 當前進入寫鎖 read...
並且程序一直在等待,嘗試獲取writeLock。