一、概念
synchronized是Java語言的關(guān)鍵字,是內(nèi)置特性,而ReentrantLock是一個(gè)實(shí)現(xiàn)了Lock接口的類,通過該類可以實(shí)現(xiàn)線程的同步。
synchronized和ReentrantLock都是可重入鎖。當(dāng)一個(gè)線程執(zhí)行到某個(gè)synchronized方法時(shí),比如method1,而在method1中會(huì)調(diào)用另外一個(gè)synchronized方法,比如method2,此時(shí)線程不必重新去申請(qǐng)鎖,而是可以直接執(zhí)行方法method2。
二、ReentrantLock
主要方法:
lock():獲取不到鎖就不罷休,否則線程一直處于阻塞狀態(tài)。
tryLock():嘗試性地獲取鎖,不管有沒有獲取到都馬上返回,拿到鎖就返回true,不然就返回false。
tryLock(long time, TimeUnit unit):如果獲取不到鎖,就等待一段時(shí)間,超時(shí)返回false。
lockInterruptibly():線程調(diào)用lockInterruptibly()方法獲取鎖,未獲取到處于阻塞狀態(tài),此時(shí)如果調(diào)用該線程的interrupt()方法會(huì)喚醒該線程并要求處理InterruptedException。
static class MyThread extends Thread {
private static Lock lock = new ReentrantLock();
private MyThread(String name) {
setName(name);
}
@Override
public void run() {
Log.d(TAG, "zwm, thread: " + getName() + " run start");
try {
lock.lockInterruptibly();
Log.d(TAG, "zwm, thread: " + getName() + " acquire lock");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
Log.d(TAG, "zwm, thread: " + getName() + " InterruptedException");
} finally {
try {
lock.unlock();
Log.d(TAG, "zwm, thread: " + getName() + " release lock");
} catch (IllegalMonitorStateException e) {
e.printStackTrace();
Log.d(TAG, "zwm, thread: " + getName() + " IllegalMonitorStateException");
}
}
Log.d(TAG, "zwm, thread: " + getName() + " run end");
}
}
//測(cè)試代碼
MyThread thread1 = new MyThread("thread1");
thread1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyThread thread2 = new MyThread("thread2");
thread2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.interrupt();
//輸出
02-11 21:22:13.046 zwm, thread: thread1 run start
02-11 21:22:13.046 zwm, thread: thread1 acquire lock
02-11 21:22:14.046 zwm, thread: thread2 run start
02-11 21:22:15.048 zwm, thread: thread2 InterruptedException
02-11 21:22:15.049 zwm, thread: thread2 IllegalMonitorStateException
02-11 21:22:15.049 zwm, thread: thread2 run end
02-11 21:22:23.047 zwm, thread: thread1 release lock
02-11 21:22:23.047 zwm, thread: thread1 run end
三、ReentrantReadWriteLock
可重入讀寫鎖,實(shí)現(xiàn)了ReadWriteLock接口:
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
現(xiàn)實(shí)中有這樣一種場(chǎng)景:對(duì)共享資源有讀和寫的操作,且寫操作沒有讀操作那么頻繁。在沒有寫操作的時(shí)候,多個(gè)線程同時(shí)讀一個(gè)資源沒有任何問題,所以應(yīng)該允許多個(gè)線程同時(shí)讀取共享資源;但是如果一個(gè)線程想去寫這些共享資源,就不應(yīng)該允許其他線程對(duì)該資源進(jìn)行讀和寫的操作了。
針對(duì)這種場(chǎng)景,Java的并發(fā)包提供了讀寫鎖ReentrantReadWriteLock,它表示兩個(gè)鎖,一個(gè)是讀操作相關(guān)的鎖,稱為共享鎖;一個(gè)是寫相關(guān)的鎖,稱為排他鎖。
線程進(jìn)入讀鎖的前提條件:
- 沒有其他線程的寫鎖。
- 沒有寫請(qǐng)求,或者有寫請(qǐng)求但調(diào)用線程和持有鎖的線程是同一個(gè)。
線程進(jìn)入寫鎖的前提條件:
- 沒有其他線程的讀鎖。
- 沒有其他線程的寫鎖。
讀寫鎖有以下三個(gè)重要的特性:
- 公平選擇性:支持非公平(默認(rèn))和公平的鎖獲取方式,吞吐量還是非公平優(yōu)于公平。
- 重進(jìn)入:讀鎖和寫鎖都支持線程重進(jìn)入。
- 鎖降級(jí):遵循獲取寫鎖、獲取讀鎖、再釋放寫鎖的次序,寫鎖能夠降級(jí)成為讀鎖。
讀鎖:
static class MyThread extends Thread{
private static ReentrantReadWriteLock.ReadLock lock = new ReentrantReadWriteLock().readLock();
private MyThread(String name) {
setName(name);
}
@Override
public void run() {
Log.d(TAG, "zwm, thread: " + getName() + " run start");
lock.lock();
Log.d(TAG, "zwm, thread: " + getName() + " acquire lock");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
Log.d(TAG, "zwm, thread: " + getName() + " release lock");
}
Log.d(TAG, "zwm, thread: " + getName() + " run end");
}
}
//測(cè)試代碼
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.start();
MyThread thread3 = new MyThread("thread3");
thread3.start();
//輸出
02-11 22:52:10.112 zwm, thread: thread1 run start
02-11 22:52:10.112 zwm, thread: thread1 acquire lock
02-11 22:52:10.113 zwm, thread: thread2 run start
02-11 22:52:10.113 zwm, thread: thread3 run start
02-11 22:52:10.113 zwm, thread: thread2 acquire lock
02-11 22:52:10.113 zwm, thread: thread3 acquire lock
02-11 22:52:12.113 zwm, thread: thread1 release lock
02-11 22:52:12.113 zwm, thread: thread1 run end
02-11 22:52:12.114 zwm, thread: thread3 release lock
02-11 22:52:12.114 zwm, thread: thread3 run end
02-11 22:52:12.114 zwm, thread: thread2 release lock
02-11 22:52:12.114 zwm, thread: thread2 run end
寫鎖:
static class MyThread extends Thread{
private static ReentrantReadWriteLock.WriteLock lock = new ReentrantReadWriteLock().writeLock();
private MyThread(String name) {
setName(name);
}
@Override
public void run() {
Log.d(TAG, "zwm, thread: " + getName() + " run start");
lock.lock();
Log.d(TAG, "zwm, thread: " + getName() + " acquire lock");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
Log.d(TAG, "zwm, thread: " + getName() + " release lock");
}
Log.d(TAG, "zwm, thread: " + getName() + " run end");
}
}
//測(cè)試代碼
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.start();
MyThread thread3 = new MyThread("thread3");
thread3.start();
//輸出
02-11 22:55:44.916 zwm, thread: thread3 run start
02-11 22:55:44.917 zwm, thread: thread3 acquire lock
02-11 22:55:44.917 zwm, thread: thread2 run start
02-11 22:55:44.917 zwm, thread: thread1 run start
02-11 22:55:46.917 zwm, thread: thread2 acquire lock
02-11 22:55:46.917 zwm, thread: thread3 release lock
02-11 22:55:46.918 zwm, thread: thread3 run end
02-11 22:55:48.918 zwm, thread: thread1 acquire lock
02-11 22:55:48.918 zwm, thread: thread2 release lock
02-11 22:55:48.919 zwm, thread: thread2 run end
02-11 22:55:50.919 zwm, thread: thread1 release lock
02-11 22:55:50.919 zwm, thread: thread1 run end
四、Lock與synchronized的比較
- Lock是一個(gè)接口,而synchronized是Java中的關(guān)鍵字,synchronized是內(nèi)置的語言實(shí)現(xiàn)。
- synchronized在發(fā)生異常時(shí),會(huì)自動(dòng)釋放線程占有的鎖,因此不會(huì)導(dǎo)致死鎖現(xiàn)象發(fā)生。Lock在發(fā)生異常時(shí),如果沒有主動(dòng)通過unLock()方法去釋放鎖,則很可能造成死鎖的現(xiàn)象,因此使用Lock時(shí)需要在finally塊中釋放鎖。
- Lock可以讓等待鎖的線程響應(yīng)中斷,而synchronized卻不行,使用synchronized時(shí),等待的線程會(huì)一直等待下去,不能夠響應(yīng)中斷。
- 通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。
- Lock可以提高多個(gè)線程進(jìn)行讀操作的效率。
五、Condition
Lock用于控制多線程對(duì)同一狀態(tài)的順序訪問,保證該狀態(tài)的連續(xù)性。
Condition用于控制多線程之間的基于該狀態(tài)的條件等待。
生產(chǎn)者消費(fèi)者簡(jiǎn)單模型:生產(chǎn)者往buffer里put,消費(fèi)者從buffer里take。
1.同一狀態(tài)的順序訪問
有三個(gè)狀態(tài)需要順序訪問:buffer的大小count,生產(chǎn)者用于put的游標(biāo)putptr,消費(fèi)者用于take的游標(biāo)takeptr。
2.基于該狀態(tài)的條件等待
當(dāng)count = 0時(shí),消費(fèi)者的take需要等待;當(dāng)count = buffer.size時(shí),生產(chǎn)者的put需要等待。
使用Lock & Condition實(shí)現(xiàn)生產(chǎn)者消費(fèi)者簡(jiǎn)單模型:
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[5];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
Log.d(TAG, "zwm, put method, thread: " + Thread.currentThread().getName() + " acquire lock");
try {
while (count == items.length) {
Log.d(TAG, "zwm, buffer full, thread: " + Thread.currentThread().getName() + " await using notFull condition");
notFull.await();
}
items[putptr] = x;
Log.d(TAG, "zwm, thread: " + Thread.currentThread().getName() + " putptr: " + putptr + ", value: " + items[putptr]);
if (++putptr == items.length) putptr = 0;
++count;
Log.d(TAG, "zwm, put done, thread: " + Thread.currentThread().getName() + " signal other thread using notEmpty condition");
notEmpty.signal();
} finally {
lock.unlock();
Log.d(TAG, "zwm, put method, thread: " + Thread.currentThread().getName() + " release lock");
}
}
public Object take() throws InterruptedException {
lock.lock();
Log.d(TAG, "zwm, take method, thread: " + Thread.currentThread().getName() + " acquire lock");
try {
while (count == 0) {
Log.d(TAG, "zwm, buffer empty, thread: " + Thread.currentThread().getName() + " await using notEmpty condition");
notEmpty.await();
}
Object x = items[takeptr];
Log.d(TAG, "zwm, thread: " + Thread.currentThread().getName() + " takeptr: " + takeptr + ", value: " + items[takeptr]);
if (++takeptr == items.length) takeptr = 0;
--count;
Log.d(TAG, "zwm, take done, thread: " + Thread.currentThread().getName() + " signal other thread using notFull condition");
notFull.signal();
return x;
} finally {
lock.unlock();
Log.d(TAG, "zwm, take method, thread: " + Thread.currentThread().getName() + " release lock");
}
}
}
class PutThread extends Thread {
private BoundedBuffer buffer;
public PutThread(String name, BoundedBuffer buffer) {
setName(name);
this.buffer = buffer;
}
@Override
public void run() {
for(int i=0; i<5; i++) {
try {
buffer.put(String.valueOf(i));
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TakeThread extends Thread {
private BoundedBuffer buffer;
public TakeThread(String name, BoundedBuffer buffer) {
setName(name);
this.buffer = buffer;
}
@Override
public void run() {
for(int i=0; i<5; i++) {
try {
buffer.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//測(cè)試代碼
BoundedBuffer buffer = new BoundedBuffer();
PutThread putThread1 = new PutThread("putThread1", buffer);
putThread1.start();
TakeThread takeThread1 = new TakeThread("takeThread1", buffer);
takeThread1.start();
TakeThread takeThread2 = new TakeThread("takeThread2", buffer);
takeThread2.start();
//輸出
2019-02-12 14:27:44.717 zwm, put method, thread: putThread1 acquire lock
2019-02-12 14:27:44.717 zwm, thread: putThread1 putptr: 0, value: 0
2019-02-12 14:27:44.717 zwm, put done, thread: putThread1 signal other thread using notEmpty condition
2019-02-12 14:27:44.717 zwm, put method, thread: putThread1 release lock
2019-02-12 14:27:44.718 zwm, take method, thread: takeThread1 acquire lock
2019-02-12 14:27:44.719 zwm, thread: takeThread1 takeptr: 0, value: 0
2019-02-12 14:27:44.719 zwm, take done, thread: takeThread1 signal other thread using notFull condition
2019-02-12 14:27:44.719 zwm, take method, thread: takeThread1 release lock
2019-02-12 14:27:44.719 zwm, take method, thread: takeThread2 acquire lock
2019-02-12 14:27:44.719 zwm, buffer empty, thread: takeThread2 await using notEmpty condition //buffer空,takeThread2等待,釋放鎖
2019-02-12 14:27:44.720 zwm, take method, thread: takeThread1 acquire lock //takeThread1獲取鎖
2019-02-12 14:27:44.721 zwm, buffer empty, thread: takeThread1 await using notEmpty condition //buffer空,takeThread1等待,釋放鎖
2019-02-12 14:27:45.718 zwm, put method, thread: putThread1 acquire lock
2019-02-12 14:27:45.718 zwm, thread: putThread1 putptr: 1, value: 1
2019-02-12 14:27:45.718 zwm, put done, thread: putThread1 signal other thread using notEmpty condition
2019-02-12 14:27:45.718 zwm, put method, thread: putThread1 release lock
2019-02-12 14:27:45.718 zwm, thread: takeThread2 takeptr: 1, value: 1 //takeThread2被喚醒,獲取鎖
2019-02-12 14:27:45.718 zwm, take done, thread: takeThread2 signal other thread using notFull condition
2019-02-12 14:27:45.719 zwm, take method, thread: takeThread2 release lock
2019-02-12 14:27:45.719 zwm, take method, thread: takeThread2 acquire lock //takeThread2獲取鎖
2019-02-12 14:27:45.719 zwm, buffer empty, thread: takeThread2 await using notEmpty condition //buffer空,takeThread2等待,釋放鎖
2019-02-12 14:27:46.719 zwm, put method, thread: putThread1 acquire lock
2019-02-12 14:27:46.719 zwm, thread: putThread1 putptr: 2, value: 2
2019-02-12 14:27:46.720 zwm, put done, thread: putThread1 signal other thread using notEmpty condition
2019-02-12 14:27:46.720 zwm, put method, thread: putThread1 release lock
2019-02-12 14:27:46.721 zwm, thread: takeThread1 takeptr: 2, value: 2 //takeThread1被喚醒,獲取鎖
2019-02-12 14:27:46.721 zwm, take done, thread: takeThread1 signal other thread using notFull condition
2019-02-12 14:27:46.721 zwm, take method, thread: takeThread1 release lock
2019-02-12 14:27:46.722 zwm, take method, thread: takeThread1 acquire lock //takeThread1獲取鎖
2019-02-12 14:27:46.722 zwm, buffer empty, thread: takeThread1 await using notEmpty condition //buffer空,takeThread1等待,釋放鎖
2019-02-12 14:27:47.721 zwm, put method, thread: putThread1 acquire lock
2019-02-12 14:27:47.721 zwm, thread: putThread1 putptr: 3, value: 3
2019-02-12 14:27:47.721 zwm, put done, thread: putThread1 signal other thread using notEmpty condition
2019-02-12 14:27:47.721 zwm, put method, thread: putThread1 release lock
2019-02-12 14:27:47.722 zwm, thread: takeThread2 takeptr: 3, value: 3
2019-02-12 14:27:47.722 zwm, take done, thread: takeThread2 signal other thread using notFull condition
2019-02-12 14:27:47.722 zwm, take method, thread: takeThread2 release lock
2019-02-12 14:27:47.722 zwm, take method, thread: takeThread2 acquire lock
2019-02-12 14:27:47.722 zwm, buffer empty, thread: takeThread2 await using notEmpty condition //buffer空,takeThread2等待,釋放鎖
2019-02-12 14:27:48.722 zwm, put method, thread: putThread1 acquire lock
2019-02-12 14:27:48.722 zwm, thread: putThread1 putptr: 4, value: 4
2019-02-12 14:27:48.722 zwm, put done, thread: putThread1 signal other thread using notEmpty condition
2019-02-12 14:27:48.723 zwm, put method, thread: putThread1 release lock
2019-02-12 14:27:48.723 zwm, thread: takeThread1 takeptr: 4, value: 4
2019-02-12 14:27:48.723 zwm, take done, thread: takeThread1 signal other thread using notFull condition
2019-02-12 14:27:48.724 zwm, take method, thread: takeThread1 release lock
2019-02-12 14:27:48.724 zwm, take method, thread: takeThread1 acquire lock
2019-02-12 14:27:48.724 zwm, buffer empty, thread: takeThread1 await using notEmpty condition //buffer空,takeThread1等待,釋放鎖