第4章 Lock的使用
在Java多線程中, 可以使用synchronized關(guān)鍵字來實(shí)現(xiàn)線程之間同步互斥,但在JDK1.5中新增加了
ReentrantLock
類也能達(dá)到同樣的效果,并且在擴(kuò)展功能上也更加強(qiáng)大。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*/
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod() {
lock.lock();
for (int i = 0; i < 5; i++)
System.out.println("ThreadName=" + Thread.currentThread().getName() + (" " + (i + 1)));
lock.unlock();
}
}
class MyThread extends Thread {
private MyService service;
public MyThread(MyService service) {
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
class Run {
public static void main(String[] args) {
MyService service = new MyService();
MyThread t1 = new MyThread(service);
MyThread t2 = new MyThread(service);
MyThread t3 = new MyThread(service);
MyThread t4 = new MyThread(service);
MyThread t5 = new MyThread(service);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
OUTPUT:
ThreadName=Thread-0 1
ThreadName=Thread-0 2
ThreadName=Thread-0 3
ThreadName=Thread-0 4
ThreadName=Thread-0 5
ThreadName=Thread-2 1
ThreadName=Thread-2 2
ThreadName=Thread-2 3
ThreadName=Thread-2 4
ThreadName=Thread-2 5
ThreadName=Thread-3 1
ThreadName=Thread-3 2
ThreadName=Thread-3 3
ThreadName=Thread-3 4
ThreadName=Thread-3 5
ThreadName=Thread-4 1
ThreadName=Thread-4 2
ThreadName=Thread-4 3
ThreadName=Thread-4 4
ThreadName=Thread-4 5
ThreadName=Thread-1 1
ThreadName=Thread-1 2
ThreadName=Thread-1 3
ThreadName=Thread-1 4
ThreadName=Thread-1 5
調(diào)用
lock.lock()
代碼的線程就持有了“對象監(jiān)視器”,其他線程只有等待鎖被釋放時(shí)再次爭搶。效果和使用synchronized關(guān)鍵字一樣,線程之間執(zhí)行的順序是隨機(jī)的。Condition
對象的await()
方法,使當(dāng)前執(zhí)行任務(wù)的線程進(jìn)入了等待WAITING狀態(tài)。Condition
實(shí)現(xiàn)等待 / 通知
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//在調(diào)用condition.await()方法之前需調(diào)用lock.lock()代碼獲得同步監(jiān)視器
public class MyService3 {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("A");
condition.await();//等待
System.out.println("B");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void single() {
try {
lock.lock();
condition.signal();//喚醒
} finally {
lock.unlock();
}
}
}
class Run3 {
public static void main(String[] args) throws InterruptedException {
final MyService3 service = new MyService3();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
service.await();
}
});
thread.start();
System.out.println(Thread.currentThread().getName());
Thread.sleep(1);
service.single();
}
}
- 在使用
notify()/notifyAll()
方法進(jìn)行通知時(shí),被通知的線程卻是由JVM隨機(jī)選擇的。但使用ReentrantLock結(jié)合Condition類是可以實(shí)現(xiàn)"選擇性通知“。
Object類中的
wait()
方法相當(dāng)于Condition類中的await()
方法Object類中的
wait(long timeout)
方法相當(dāng)于Condition類中的`await(long time, TimeUnit unit)方法Object類中的
notify()
方法相當(dāng)于Condition類中的signal()
方法。Object類中的
notifyAll()
方法相當(dāng)于Condition類中的signalAll()
方法。
- 公平與非wcguqim:鎖Lock分為“公平鎖”和“非公平鎖”, 公平鎖表示線程獲取鎖的順序是按照線程加鎖的順序來分配的,即先來先得的FIFO先進(jìn)先出順序。而非公平鎖就是一種獲取鎖的搶占機(jī)制,是隨機(jī)獲得鎖的,和公平鎖不一樣的就是先來不一定先得到鎖,這個(gè)方式可能造成某些線程一直拿不鎖,結(jié)果也就是不公平的了。
getHoldCount(), getQueueLength(), getWaitQueueLength()
- 方法
int getHoldCount()
的作用是查詢當(dāng)前線程保持此鎖定的個(gè)數(shù),也就是調(diào)用lock()
方法的次數(shù)。
lock.getHoldCount();
- 方法
int getQueueLength()
的作用是返回正等待獲取此鎖定的線程估計(jì)數(shù),比如有5個(gè)線程,1個(gè)線程首先執(zhí)行await()
方法,那么在調(diào)用getQueueLength()
方法反返回值是4,說明有4個(gè)線程同時(shí)在等待lock的釋放。
lock.getQueueLength();
- 方法
int getWaitQueueLength(Condition condition)
的作用是返回等待與此鎖定相關(guān)的給定條件Condition的線程估計(jì)數(shù),比如有5個(gè)線程,每個(gè)線程都執(zhí)行了同一個(gè)condition對象的await()
方法,則調(diào)用getWaitQueueLength(Condition condition)
方法時(shí)返回的int值是5。
hasQueuedThread(), hasQueuedThreads(), hasWaiters()
方法
boolean hasQueuedThread(thtread thread)
的作用是查詢指定的線程是否正在等待獲取此鎖定。方法
boolean hasQueuedThreads()
的作用是查詢是否有線程正在等待獲取此鎖定。
lock.hasQueuedThread(threadA);
lock.hasQueuedThreads();
- 方法
boolean hasWaiters(Condition condition)
的作用是查詢是否有線程正在等待與此鎖定有關(guān)的condition條件。
lock.hasWaiters(newConditino);
isFair(), isHeldByCurrentThread(), isLocked()
- 方法
boolean isFair()
的作用是判斷是不是公平鎖。在默認(rèn)的情況下,ReentrantLock類使用的是非公平鎖。
lock.isFair();
- 方法
boolean isHeldByCurrentThread()
的作用是查詢當(dāng)前線程是否保持此鎖定。
lock.isHeldByCurrentThread();
- 方法
boolean isLocked()
的作用是查詢此鎖定是否由任意線程保持。
lock.isLocked();
lockInterruptibly(), tryLock(), tryLock(long timeout,TimeUnit unit)
方法
void lockInterruptibly()
的作用是:如果當(dāng)前線程未衩中斷,則獲取鎖定,如果已經(jīng)被中斷則出現(xiàn)異常。方法
boolean tryLock()
的作用是,僅在調(diào)用時(shí)鎖定未被另一個(gè)線程保持的情況下,才獲取該鎖定。方法
boolean tryLock(long timeout, TimeUnit unit)
的作用是,如果鎖定在給定等待時(shí)間內(nèi)沒有被別一個(gè)線程保持,且當(dāng)前線程未被中斷,則獲取該鎖定。
ReentrantReadWriteLock
類ReentrantLock具有完全互斥排他的效果,即同一時(shí)間只有一個(gè)線程在執(zhí)行ReentrantLock.lock()方法后面的任務(wù)。這樣做雖然保證了實(shí)例變量的線程安全性,但效率卻是非常低下的。
所以在JDK中提供了一種讀寫鎖ReentrantReadWriteLock類,使用它可以加快運(yùn)行效率,在某些不需要操作實(shí)例變量的方法中,完全可以使用讀寫鎖ReentratnReadWriteLock來提升該方法的代碼運(yùn)行速度。
永定鎖表示也有兩個(gè)鎖,一個(gè)是讀操作相關(guān)的鎖,也稱為共享鎖;另一個(gè)是寫操作相關(guān)的鎖,也叫排他鎖。也就是多個(gè)讀鎖之間不互斥,寫寫、寫讀互斥。
...
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
...
lock.writeLock().lock();
lock.readLock().lock();
...
lock.wirteLock().unlock();
lock.readLock().unlock();