內(nèi)容簡介
ReentrantLock類的使用。
ReentrantReadWriteLock類的使用。
4.1 使用ReentrantLock類
在多線程中,可以使用synchronized來實(shí)現(xiàn)線程之間的同步互斥,在JDK1.5中新增了ReentrantLock類也可打到同樣的效果,并在拓展功能上更強(qiáng)大,比如:嗅探鎖定、多路分支通知等,使用上比synchronized更加靈活,下面我們在學(xué)習(xí)中來任務(wù)兩者的異同吧!
4.1.1 使用ReentrantLock實(shí)現(xiàn)同步
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod() {
lock.lock();
for (int i = 0; i < 2; i++) {
System.out.println("ThreadName" + Thread.currentThread().getName() + " " + i);
}
lock.unlock();
}
}
lock()獲取鎖,unlock()釋放鎖。
public class MyThread extends Thread {
private MyService myService;
public MyThread(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.testMethod();
}
}
public class Run {
public static void main(String[] args) {
MyService myService = new MyService();
MyThread m1 = new MyThread(myService);
MyThread m2 = new MyThread(myService);
MyThread m3 = new MyThread(myService);
MyThread m4 = new MyThread(myService);
MyThread m5 = new MyThread(myService);
m1.start();
m2.start();
m3.start();
m4.start();
m5.start();
}
}
ThreadNameThread-1 0
ThreadNameThread-1 1
ThreadNameThread-0 0
ThreadNameThread-0 1
ThreadNameThread-3 0
ThreadNameThread-3 1
ThreadNameThread-4 0
ThreadNameThread-4 1
ThreadNameThread-2 0
ThreadNameThread-2 1
從打印結(jié)果來看,當(dāng)一個(gè)線程調(diào)用unlock()方法釋放鎖后,其他線程搶到鎖繼續(xù)執(zhí)行,下面我們繼續(xù)做個(gè)試驗(yàn):
public class MyService {
private Lock lock = new ReentrantLock();
public void methodA() {
try {
lock.lock();
System.out.println("methodA begin threadNAME=" + Thread.currentThread().getName() + " Time=" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("methodA end threadNAME=" + Thread.currentThread().getName() + " Time=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void methodB() {
try {
lock.lock();
System.out.println("methodB begin threadNAME=" + Thread.currentThread().getName() + " Time=" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("methodB end threadNAME=" + Thread.currentThread().getName() + " Time=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.methodA();
}
}
public class ThreadAA extends Thread {
private MyService myService;
public ThreadAA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.methodA();
}
}
public class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.methodB();
}
}
public class ThreadBB extends Thread {
private MyService myService;
public ThreadBB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.methodB();
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
ThreadA a = new ThreadA(myService);
a.setName("a");
a.start();
ThreadAA aa = new ThreadAA(myService);
aa.setName("aa");
aa.start();
Thread.sleep(100);
ThreadB b = new ThreadB(myService);
b.setName("b");
b.start();
ThreadBB bb = new ThreadBB(myService);
bb.setName("bb");
bb.start();
}
}
methodA begin threadNAME=a Time=1590990695499
methodA end threadNAME=a Time=1590990697502
methodA begin threadNAME=aa Time=1590990697502
methodA end threadNAME=aa Time=1590990699506
methodB begin threadNAME=bb Time=1590990699506
methodB end threadNAME=bb Time=1590990701507
methodB begin threadNAME=b Time=1590990701507
methodB end threadNAME=b Time=1590990703509
驗(yàn)證結(jié)果:調(diào)用lock()代碼的線程就持有了對(duì)象監(jiān)視器,其他線程只有等待持有鎖的線程釋放。
4.1.2 使用Condition實(shí)現(xiàn)等待/通知
synchronized與wait(),notify()或notifyAll()方法相結(jié)合,可以實(shí)現(xiàn)等待/通知模式。(對(duì)wait(),notify()或notifyAll()需要了解的可以看看一文詳解wait與notify)
ReentrantLock也可以實(shí)現(xiàn)同樣的功能,但需要借助與Condition對(duì)象。Condition是JDK5中出現(xiàn)的,它有更好的靈活性,比如實(shí)現(xiàn)多路通知功能,也就是在一個(gè)Lock對(duì)象里面可以創(chuàng)建多個(gè)Condition(即對(duì)象監(jiān)視器)實(shí)例,線程對(duì)象可以注冊在指定的Condition中,從而有選擇的進(jìn)行通知,在調(diào)度線程上更加靈活。
使用nodify()/notifyAll()方法進(jìn)行通知時(shí),被通知的線程是JVM隨機(jī)選擇的。ReentrantLock結(jié)合Condition類是可以實(shí)現(xiàn)選擇性通知。
synchronize就相當(dāng)于整個(gè)Lock對(duì)象中只有一個(gè)單一的Condition對(duì)象,所有線程都注冊在它一個(gè)對(duì)象的身上,notifyAll()時(shí),需要通知所有的等待該對(duì)象的線程,JVM隨機(jī)喚醒其中一個(gè)。
調(diào)用Condition.await方法前,如果沒有持有合適的鎖,則會(huì)報(bào)
java.lang.IllegalMonitorStateException的異常,我們可以通過ReentrantLock.lock()來獲取鎖。
下面我們使用Condition來實(shí)現(xiàn)一個(gè)等待通知模式:
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("await start 時(shí)間為:" + System.currentTimeMillis());
condition.await();
System.out.println("await end 時(shí)間為:" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signal(){
try {
lock.lock();
System.out.println("signal時(shí)間為:"+System.currentTimeMillis());
condition.signal();
}finally {
lock.unlock();
}
}
}
public class MyThread extends Thread {
private MyService myService;
public MyThread(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.await();
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
MyThread myThread = new MyThread(myService);
myThread.start();
Thread.sleep(3000);
myService.signal();
}
}
await start 時(shí)間為:1590995422964
signal時(shí)間為:1590995425971
await end 時(shí)間為:1590995425971
1)Object中的
wait()方法相當(dāng)于Condition類中的await()。
2)Object中的wait(long timeout)相當(dāng)于Condition中的await(long time,TimeUnit unit)。
3)Object中的notify()相當(dāng)于Condition中的signal()。
4)Object中的notifyAll()相當(dāng)于Condition中的signalAll()。
4.1.3 使用多個(gè)Condition實(shí)現(xiàn)通知部分線程
如何使用單個(gè)Condition來通知所有線程呢?
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
condition.await();
System.out.println(" end awaitA time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
condition.await();
System.out.println(" end awaitB time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalAll() {
try {
lock.lock();
System.out.println(" signalAll time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
condition.signalAll();
} finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.awaitA();
}
}
public class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.awaitB();
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
ThreadA a = new ThreadA(myService);
a.setName("a");
a.start();
ThreadB b = new ThreadB(myService);
b.setName("b");
b.start();
Thread.sleep(2000);
myService.signalAll();
}
}
begin awaitB time = 1590998606632 thread Name = b
begin awaitA time = 1590998606632 thread Name = a
signalAll time = 1590998608626 thread Name = main
end awaitB time = 1590998608626 thread Name = b
end awaitA time = 1590998608626 thread Name = a
那么如何使用多個(gè)Condition通知部分線程呢?
public class MyService {
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
conditionA.await();
System.out.println(" end awaitA time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
conditionB.await();
System.out.println(" end awaitB time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalAllA() {
try {
lock.lock();
System.out.println(" signalAllA time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
conditionA.signalAll();
} finally {
lock.unlock();
}
}
public void signalAllB() {
try {
lock.lock();
System.out.println(" signalAllB time = " + System.currentTimeMillis() + " thread Name = " + Thread.currentThread().getName());
conditionA.signalAll();
} finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.awaitA();
}
}
public class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.awaitB();
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
ThreadA a = new ThreadA(myService);
a.setName("a");
a.start();
ThreadB b = new ThreadB(myService);
b.setName("b");
b.start();
Thread.sleep(1000);
myService.signalAllA();
Thread.sleep(1000);
myService.signalAllB();
}
}
begin awaitB time = 1590999063461 thread Name = b
begin awaitA time = 1590999063485 thread Name = a
signalAllA time = 1590999064453 thread Name = main
end awaitA time = 1590999064453 thread Name = a
signalAllB time = 1590999065453 thread Name = main
4.1.4 使用Condition實(shí)現(xiàn)生產(chǎn)者/消費(fèi)者模式
單生產(chǎn)者與單消費(fèi)者代碼示例:
public class MyService {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set() {
try {
lock.lock();
while (hasValue == true) {
condition.await();
}
System.out.println("打印★");
hasValue = true;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (hasValue == false) {
condition.await();
}
System.out.println("打印☆");
hasValue = false;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.set();
}
}
}
public class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.get();
}
}
}
public class Run {
public static void main(String[] args) {
MyService myService = new MyService();
ThreadA a = new ThreadA(myService);
a.start();
ThreadB b = new ThreadB(myService);
b.start();
}
}
···
打印☆
打印★
打印☆
打印★
打印☆
打印★
···
那么多生產(chǎn)與多消費(fèi)怎么控制呢 ?
public class MyService {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set() {
try {
lock.lock();
while (hasValue == true) {
System.out.println("有可能★★連續(xù)");
condition.await();
}
System.out.println("打印★");
hasValue = true;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (hasValue == false) {
System.out.println("有可能☆☆連續(xù)");
condition.await();
}
System.out.println("打印☆");
hasValue = false;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.set();
}
}
}
public class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.get();
}
}
}
public class Run {
public static void main(String[] args) {
MyService myService = new MyService();
ThreadA[] a = new ThreadA[10];
ThreadB[] b = new ThreadB[10];
for (int i = 0; i < 10; i++) {
a[i] = new ThreadA(myService);
b[i] = new ThreadB(myService);
a[i].start();
b[i].start();
}
}
}
...
打印★
有可能★★連續(xù)
打印☆
打印★
有可能★★連續(xù)
有可能★★連續(xù)
有可能★★連續(xù)
有可能★★連續(xù)
有可能★★連續(xù)
有可能★★連續(xù)
打印☆
有可能☆☆連續(xù)
...
4.1.9 公平鎖與非公平鎖
Lock分為公平鎖和非公平鎖。
公平鎖:線程獲取鎖的順序是按照線程加鎖的順序來分配的,即FIFO(先進(jìn)先出)。
非公平鎖:隨機(jī)獲得鎖。
那么下面我們就用代碼來了解兩種鎖的特性吧。
非公平鎖示例:
public class Service {
private ReentrantLock lock;
public Service(boolean isFair) {
this.lock = new ReentrantLock(isFair);
}
public void serviceMethod(){
try {
lock.lock();
System.out.println("ThreadName="+Thread.currentThread().getName()+"獲取鎖");
}finally {
lock.unlock();
}
}
}
public class Run {
public static void main(String[] args) {
final Service service = new Service(false);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("★線程" + Thread.currentThread().getName() + "開始執(zhí)行");
service.serviceMethod();
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
}
}
★線程Thread-0開始執(zhí)行
ThreadName=Thread-0獲取鎖
★線程Thread-2開始執(zhí)行
★線程Thread-1開始執(zhí)行
ThreadName=Thread-2獲取鎖
★線程Thread-4開始執(zhí)行
ThreadName=Thread-4獲取鎖
★線程Thread-5開始執(zhí)行
★線程Thread-8開始執(zhí)行
ThreadName=Thread-5獲取鎖
★線程Thread-9開始執(zhí)行
ThreadName=Thread-9獲取鎖
★線程Thread-3開始執(zhí)行
ThreadName=Thread-3獲取鎖
ThreadName=Thread-1獲取鎖
ThreadName=Thread-8獲取鎖
★線程Thread-7開始執(zhí)行
ThreadName=Thread-7獲取鎖
★線程Thread-6開始執(zhí)行
ThreadName=Thread-6獲取鎖
非公平鎖的運(yùn)行結(jié)果基本上是亂序的,先啟動(dòng)的線程不代表先獲得鎖。
公平鎖示例:
修改Run.java代碼如下
public class Run {
public static void main(String[] args) {
final Service service = new Service(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("★線程" + Thread.currentThread().getName() + "開始執(zhí)行");
service.serviceMethod();
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
}
}
★線程Thread-1開始執(zhí)行
★線程Thread-2開始執(zhí)行
ThreadName=Thread-1獲取鎖
★線程Thread-5開始執(zhí)行
★線程Thread-6開始執(zhí)行
ThreadName=Thread-2獲取鎖
ThreadName=Thread-5獲取鎖
★線程Thread-0開始執(zhí)行
★線程Thread-4開始執(zhí)行
ThreadName=Thread-6獲取鎖
★線程Thread-8開始執(zhí)行
ThreadName=Thread-0獲取鎖
★線程Thread-9開始執(zhí)行
ThreadName=Thread-4獲取鎖
ThreadName=Thread-8獲取鎖
ThreadName=Thread-9獲取鎖
★線程Thread-3開始執(zhí)行
ThreadName=Thread-3獲取鎖
★線程Thread-7開始執(zhí)行
ThreadName=Thread-7獲取鎖
觀察執(zhí)行結(jié)果可以發(fā)現(xiàn),啟動(dòng)的順序與獲得鎖的順序一致。
4.1.10 ReentrantLock中方法的使用
4.1.10.1 getHoldCount()
getHoldCount()查詢當(dāng)前線程保持鎖定的個(gè)數(shù),也就是獲取當(dāng)前線程調(diào)用lock()的次數(shù)。
示例代碼:
public class Service {
private ReentrantLock lock = new ReentrantLock();
private void serviceMethod1() {
try {
lock.lock();
System.out.println("serviceMethod1 getHoldCount=" + lock.getHoldCount());
serviceMethod2();
} finally {
lock.unlock();
}
}
private void serviceMethod2() {
try {
lock.lock();
System.out.println("serviceMethod2 getHoldCount=" + lock.getHoldCount());
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
Service service = new Service();
service.serviceMethod1();
}
}
serviceMethod1 getHoldCount=1
serviceMethod2 getHoldCount=2
4.1.10.2 getQueueLength()
getQueueLength()返回正等待獲取此鎖定的線程估計(jì)數(shù)。
代碼示例:
public class Service {
private ReentrantLock lock = new ReentrantLock();
public void serviceMethod1() {
try {
lock.lock();
System.out.println("ThreadName=" + Thread.currentThread().getName());
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
@Override
public void run() {
service.serviceMethod1();
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
Thread.sleep(2000);
System.out.println("有" + service.lock.getQueueLength() + "個(gè)線程在等待獲取鎖!");
}
}
ThreadName=Thread-0
有9個(gè)線程在等待獲取鎖!
···
4.1.10.3 getWaitQueueLength(Condition condition)
getWaitQueueLength(Condition condition) 返回等待與此鎖定相關(guān)的給定條件的Condition的線程估計(jì)數(shù)。
代碼示例:
public class Service {
private ReentrantLock lock = new ReentrantLock();
private Condition newCondition = lock.newCondition();
public void waitMethod() {
try {
lock.lock();
newCondition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void notifyMethod() {
try {
lock.lock();
System.out.println("有" + lock.getWaitQueueLength(newCondition) + "個(gè)線程正在等待newCondition");
newCondition.signalAll();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
@Override
public void run() {
service.waitMethod();
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
Thread.sleep(2000);
service.notifyMethod();
}
}
有10個(gè)線程正在等待newCondition
4.1.10.4 hasQueuedThreads()、hasQueuedThread()
boolean hasQueuedThread(Thread thread)查詢指定線程是否正在等待獲取此鎖定。
boolean hasQueuedThreads()查詢是否有線程正在等待獲取此鎖定。
代碼示例:
public class Service {
private ReentrantLock lock = new ReentrantLock();
public void waitMethod() {
try {
lock.lock();
System.out.println("threadName="+ Thread.currentThread().getName());
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
@Override
public void run() {
service.waitMethod();
}
};
Thread threadA = new Thread(runnable);
threadA.start();
Thread.sleep(500);
Thread threadB = new Thread(runnable);
threadB.start();
Thread.sleep(500);
System.out.println(service.lock.hasQueuedThread(threadA));
System.out.println(service.lock.hasQueuedThread(threadB));
System.out.println(service.lock.hasQueuedThreads());
}
}
threadName=Thread-0
false
true
true
threadName=Thread-1
4.1.10.5 hasWaiters()
查詢是否有線程正在等候與此鎖定有關(guān)的condition條件。
代碼示例:
public class Service {
private ReentrantLock lock = new ReentrantLock();
private Condition newCondition = lock.newCondition();
public void waitMethod() {
try {
lock.lock();
newCondition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void notifyMethod() {
try {
lock.lock();
if (lock.hasWaiters(newCondition)) {
System.out.println("存在正在等待newCondition的線程,線程數(shù):" + lock.getWaitQueueLength(newCondition));
newCondition.signalAll();
}
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
@Override
public void run() {
service.waitMethod();
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
Thread.sleep(2000);
service.notifyMethod();
}
}
存在正在等待newCondition的線程,線程數(shù):10
4.1.10.6 isFair()
判斷是否是公平鎖。
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
/**
* Returns {@code true} if this lock has fairness set true.
*
* @return {@code true} if this lock has fairness set true
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
4.1.10.6 isHeldByCurrentThread()
查詢當(dāng)前線程是否持有該鎖。
public class Service {
private ReentrantLock lock ;
public Service(boolean isFair) {
lock = new ReentrantLock(isFair);
}
public void serviceMethod(){
try {
System.out.println(lock.isHeldByCurrentThread());
lock.lock();
System.out.println(lock.isHeldByCurrentThread());
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
final Service service = new Service(true);
new Runnable() {
@Override
public void run() {
service.serviceMethod();
}
}.run();
}
}
false
true
4.1.10.7 isLocked()
查詢鎖定是否由任意線程持有。
public class Service {
private ReentrantLock lock ;
public Service(boolean isFair) {
lock = new ReentrantLock(isFair);
}
public void serviceMethod(){
try {
System.out.println(lock.isLocked());
lock.lock();
System.out.println(lock.isLocked());
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
final Service service = new Service(true);
new Runnable() {
@Override
public void run() {
service.serviceMethod();
}
}.run();
}
}
false
true
4.1.10.7 lockInterruptibly()
如果當(dāng)前線程未被中斷,則獲取鎖定,如果已經(jīng)被中斷則出現(xiàn)異常。
public class Service {
private ReentrantLock lock = new ReentrantLock();
public void serviceMethod() {
try {
lock.lockInterruptibly();
System.out.println("lock begin " + Thread.currentThread().getName());
for (int i = 0; i < Integer.MAX_VALUE / 10; i++) {
}
System.out.println("lock end " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
@Override
public void run() {
service.serviceMethod();
}
};
new Thread(runnable).start();
Thread.sleep(500);
Thread threadB = new Thread(runnable);
threadB.start();
threadB.interrupt();
}
}
lock begin Thread-0
lock end Thread-0
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
4.1.10.7 tryLock()
調(diào)用時(shí)鎖定未被另一個(gè)線程持有的情況下,才獲取該鎖定。
public class Service {
private ReentrantLock lock = new ReentrantLock();
public void getLock() {
if (lock.tryLock()) {
System.out.println(Thread.currentThread().getName() + " 獲得鎖");
} else {
System.out.println(Thread.currentThread().getName() + " 未獲得鎖");
}
}
public static void main(String[] args) {
final Service service = new Service();
Runnable runnable = new Runnable(){
@Override
public void run() {
service.getLock();
}
};
Thread threadA = new Thread(runnable);
threadA.start();
Thread threadB = new Thread(runnable);
threadB.start();
}
}
Thread-0 獲得鎖
Thread-1 未獲得鎖
4.1.10.7 tryLock(long timeout, TimeUnit unit)
如果鎖定在給定等待時(shí)間內(nèi)沒有被另一個(gè)線程持有,且當(dāng)前線程未被終端,則獲取該鎖定。
public class Service {
private ReentrantLock lock = new ReentrantLock();
public void getLock() {
try {
if (lock.tryLock(3, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread().getName() + " 獲得鎖的時(shí)間:" + System.currentTimeMillis());
Thread.sleep(4000);
} else {
System.out.println(Thread.currentThread().getName() + " 未獲得鎖時(shí)間:" + System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
public static void main(String[] args) {
final Service service = new Service();
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "調(diào)用時(shí)間:" + System.currentTimeMillis());
service.getLock();
}
};
Thread threadA = new Thread(runnable);
threadA.start();
Thread threadB = new Thread(runnable);
threadB.start();
}
}
Thread-0調(diào)用時(shí)間:1593673259015
Thread-0 獲得鎖的時(shí)間:1593673259016
Thread-1調(diào)用時(shí)間:1593673259015
Thread-1 未獲得鎖時(shí)間:1593673262017
4.1.10.7 awaitUninterruptibly()
public class Service {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void testMethod() {
try {
lock.lock();
System.out.println("wait begin");
condition.await();
System.out.println("wait end");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
@Override
public void run() {
service.testMethod();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
wait begin
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
在sleep狀態(tài)下,停止某一線程,會(huì)進(jìn)入catch語句,并清除停止?fàn)顟B(tài)值,使之變成false。對(duì)于Thread基礎(chǔ)不是很扎實(shí)的話,可以看看Thread基礎(chǔ)回顧一下。
修改代碼如下:
public class Service {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void testMethod() {
try {
lock.lock();
System.out.println("wait begin");
condition.awaitUninterruptibly();
System.out.println("wait end");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() {
@Override
public void run() {
service.testMethod();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
wait begin
對(duì)比結(jié)果:awaitUninterruptibly()與await()作用相同,但不會(huì)再等待過程中響應(yīng)中斷。
4.1.16 使用Condition實(shí)現(xiàn)順序執(zhí)行
使用Condition對(duì)象可以對(duì)線程執(zhí)行的業(yè)務(wù)進(jìn)行排序規(guī)劃。
代碼如下:
public class Run {
volatile private static int nextPrintWho = 1;
private static ReentrantLock lock = new ReentrantLock();
private static Condition conditionA = lock.newCondition();
private static Condition conditionB = lock.newCondition();
private static Condition conditionC = lock.newCondition();
public static void main(String[] args) {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
while (nextPrintWho != 1) {
conditionA.await();
}
System.out.println("threadA" + nextPrintWho);
nextPrintWho = 2;
conditionB.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
while (nextPrintWho != 2) {
conditionB.await();
}
System.out.println("threadB" + nextPrintWho);
nextPrintWho = 3;
conditionC.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
Thread threadC = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
while (nextPrintWho != 3) {
conditionC.await();
}
System.out.println("threadC" + nextPrintWho);
nextPrintWho = 1;
conditionA.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
Thread[] threadsA = new Thread[5];
Thread[] threadsB = new Thread[5];
Thread[] threadsC = new Thread[5];
for (int i = 0; i < 5; i++) {
threadsA[i] = new Thread(threadA);
threadsB[i] = new Thread(threadB);
threadsC[i] = new Thread(threadC);
threadsA[i].start();
threadsB[i].start();
threadsC[i].start();
}
}
}
threadA1
threadB2
threadC3
threadA1
threadB2
threadC3
threadA1
threadB2
threadC3
threadA1
threadB2
threadC3
threadA1
threadB2
threadC3
4.2 使用ReentrantReadWriteLock
ReentrantLock具有完全互斥排他的效果,即同一時(shí)間只有一個(gè)線程在執(zhí)行ReentrantLock.lock()方法后面的任務(wù)。這樣做雖然保證了實(shí)例變量的線程安全性,但效率卻是非常低下的,所以在JDK中提供了一種讀寫鎖ReentrantReadWriteLock類,在某些不需要操作實(shí)例變量的方法中,完全可以使用讀寫鎖來提升代碼運(yùn)行速度。
讀寫鎖表示也有兩個(gè)鎖,一個(gè)是讀操作相關(guān)的鎖,也稱為共享鎖;另一個(gè)是寫操作相關(guān)的鎖,也叫排他鎖。也就是多個(gè)讀鎖之間不互斥,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥。即多個(gè)線程可以同時(shí)進(jìn)行讀取操作,但是同一時(shí)刻只允許一個(gè)線程進(jìn)行寫操作。
4.2.1 讀讀共享
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private void read() {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " 獲取到讀鎖 " + System.currentTimeMillis());
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
public static void main(String[] args) {
final Service service = new Service();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
service.read();
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
service.read();
}
});
threadA.start();
threadB.start();
}
}
Thread-1 獲取到讀鎖 1593755228144
Thread-0 獲取到讀鎖 1593755228144
4.2.2 寫寫互斥
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private void read() {
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " 獲取到寫鎖 " + System.currentTimeMillis());
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
public static void main(String[] args) {
final Service service = new Service();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
service.read();
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
service.read();
}
});
threadA.start();
threadB.start();
}
}
Thread-0 獲取到寫鎖 1593755364185
Thread-1 獲取到讀鎖 1593755369185
writeLock()在同一時(shí)間只允許一個(gè)線程執(zhí)行l(wèi)ock后面的方法。
4.2.3 讀寫互斥
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private void read() {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " 獲取到讀鎖 " + System.currentTimeMillis());
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
private void write() {
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " 獲取到寫鎖 " + System.currentTimeMillis());
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
public static void main(String[] args) {
final Service service = new Service();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
service.read();
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
service.write();
}
});
threadA.start();
threadB.start();
}
}
Thread-0 獲取到讀鎖 1593756165771
Thread-1 獲取到寫鎖 1593756170771
4.2.4 讀寫互斥
修改以上代碼的main方法如下:
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
service.read();
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
service.write();
}
});
threadB.start();
Thread.sleep(1000);
threadA.start();
}
Thread-1 獲取到寫鎖 1593756318244
Thread-0 獲取到讀鎖 1593756323244
從以上實(shí)驗(yàn)結(jié)果看來,“讀寫”,“寫讀”,“寫寫”都是互斥的,“讀讀”是異步非互斥的。
參考文獻(xiàn)
《Java多線程編程核心技術(shù)》高紅巖