Java多线程——锁
Java多線系列文章是Java多線程的詳解介紹,對(duì)多線程還不熟悉的同學(xué)可以先去看一下我的這篇博客Java基礎(chǔ)系列3:多線程超詳細(xì)總結(jié),這篇博客從宏觀層面介紹了多線程的整體概況,接下來(lái)的幾篇文章是對(duì)多線程的深入剖析。
Lock鎖
1、簡(jiǎn)介
1、從Java5開始,Java提供了一種功能更強(qiáng)大的線程同步機(jī)制——通過(guò)顯式定義同步鎖對(duì)象來(lái)實(shí)現(xiàn)同步,在這種機(jī)制下,同步鎖由Lock對(duì)象充當(dāng)。
2、Lock 提供了比synchronized方法和synchronized代碼塊更廣泛的鎖定操作,Lock允許實(shí)現(xiàn)更靈活的結(jié)構(gòu),可以具有差別很大的屬性,并且支持多個(gè)相關(guān)的Condition對(duì)象。
3、Lock是控制多個(gè)線程對(duì)共享資源進(jìn)行訪問(wèn)的工具。通常,鎖提供了對(duì)共享資源的獨(dú)占訪問(wèn),每次只能有一個(gè)線程對(duì)Lock對(duì)象加鎖,線程開始訪問(wèn)共享資源之前應(yīng)先獲得Lock對(duì)象。
4、某些鎖可能允許對(duì)共享資源并發(fā)訪問(wèn),如ReadWriteLock(讀寫鎖),Lock、ReadWriteLock是Java5提供的兩個(gè)根接口,并為L(zhǎng)ock 提供了ReentrantLock(可重入鎖)實(shí)現(xiàn)類,為ReadWriteLock提供了ReentrantReadWriteLock 實(shí)現(xiàn)類。
5、Java8新增了新型的StampedLock類,在大多數(shù)場(chǎng)景中它可以替代傳統(tǒng)的ReentrantReadWriteLock。ReentrantReadWriteLock 為讀寫操作提供了三種鎖模式:Writing、ReadingOptimistic、Reading。
2、Lock鎖使用
class X{
//定義鎖對(duì)象
private final ReentrantLock lock=new ReentrantLock();
//定義需要保證線程安全的方法
public void m() {
//加鎖
lock.lock();
try {
//需要保證線程安全的代碼
}
finally {
lock.unlock();
}
}
}
ReentranLock
1、簡(jiǎn)介
在Java多線程中,可以使用synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)線程之間同步互斥,但在JDK1.5中新增加了ReentrantLock類也能達(dá)到同樣的效果,并且在擴(kuò)展功能上也更加強(qiáng)大,比如具有嗅探鎖定、多路分支通知等功能,而且在使用上也比synchronized更加的靈活。
2、使用ReentranLock實(shí)現(xiàn)同步
既然ReentrantLock類在功能上相比synchronized更多,那么就以一個(gè)初步的程序示例來(lái)介紹一下ReentrantLock類的使用。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; 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();
}
} public class LockTest { 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(); } }
運(yùn)行結(jié)果:
ThreadName= Thread-2 1
ThreadName= Thread-2 2
ThreadName= Thread-2 3
ThreadName= Thread-2 4
ThreadName= Thread-2 5
ThreadName= Thread-0 1
ThreadName= Thread-0 2
ThreadName= Thread-0 3
ThreadName= Thread-0 4
ThreadName= Thread-0 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
從運(yùn)行的結(jié)果來(lái)看,當(dāng)前線程打印完畢之后將鎖進(jìn)行釋放,其他線程才可以繼續(xù)打印。線程打印的數(shù)據(jù)是分組打印,因?yàn)楫?dāng)前線程已經(jīng)持有鎖,但線程之間打印的順序是隨機(jī)的。lock.lock()是對(duì)當(dāng)前線程加鎖,當(dāng)線程執(zhí)行完畢后調(diào)用lock.unlock()釋放鎖,這時(shí)候其他線程可以去獲取鎖,至于是哪一個(gè)線程可以爭(zhēng)搶到鎖還是看CPU的調(diào)度
3、使用Condition實(shí)現(xiàn)等待/通知:錯(cuò)誤用法與解決
關(guān)鍵字synchronized與wait()和notify()/notifyAll()方法相結(jié)合可以實(shí)現(xiàn)等待/通知模式,類ReentrantLock也可以實(shí)現(xiàn)同樣的功能,但需要借助于Condition對(duì)象。Condition類是在JDK5中出現(xiàn)的技術(shù),使用它有更好的靈活性,比如可以實(shí)現(xiàn)多路通知功能,也就是在一個(gè)Lock對(duì)象里面可以創(chuàng)建多個(gè)Condition(即對(duì)象監(jiān)視器)實(shí)例,線程對(duì)象可以注冊(cè)在指定的Condition中,從而可以有選擇性地進(jìn)行線程通知,在調(diào)度線程上更加靈活。
在使用notify(O/notifyAll0方法進(jìn)行通知時(shí),被通知的線程卻是由JVM隨機(jī)選擇的。但使用ReentrantLock結(jié)合Condition類是可以實(shí)現(xiàn)前面介紹過(guò)的“選擇性通知”,這個(gè)功能是非常重要的,而且在Condition類中是默認(rèn)提供的。
而synchronized就相當(dāng)于整個(gè)Lock對(duì)象中只有一個(gè)單一的Condition對(duì)象,所有的線程都注冊(cè)在它一個(gè)對(duì)象的身上。線程開始notifyAll()時(shí),需要通知所有的WAITING線程,沒(méi)有選擇權(quán),會(huì)出現(xiàn)相當(dāng)大的效率問(wèn)題。
package Thread05; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
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();
System.out.println("鎖釋放了");
}
}
} class MyThread extends Thread{
private MyService service; public MyThread(MyService service) {
this.service=service;
} @Override
public void run() {
service.await();
}
} public class LockTest { public static void main(String[] args) {
MyService service=new MyService();
MyThread thread=new MyThread(service);
thread.start(); } }
輸出結(jié)果:
A
我們可以看到輸出結(jié)果只有一個(gè)A,并沒(méi)有其他的輸出,這是因?yàn)檎{(diào)用Condition的await()方法,使當(dāng)前執(zhí)行任務(wù)的線程進(jìn)入了等待的狀態(tài)
注意:在使用Condition方法時(shí)要先調(diào)用lock.lock()代碼獲得同步監(jiān)視器
4、正確使用Condition實(shí)現(xiàn)等待/通知
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("await時(shí)間為"+System.currentTimeMillis());
condition.await();
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
System.out.println("鎖釋放了");
}
} public void signal() {
try {
lock.lock();
System.out.println("signal時(shí)間為"+System.currentTimeMillis());
condition.signal();
}finally {
lock.unlock();
}
}
} class MyThread extends Thread{
private MyService service; public MyThread(MyService service) {
this.service=service;
} @Override
public void run() {
service.await();
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThread thread=new MyThread(service);
thread.start();
Thread.sleep(3000);
service.signal(); } }
運(yùn)行結(jié)果:
await時(shí)間為1575599786039
signal時(shí)間為1575599789051
鎖釋放了
成功實(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()方法。
5、使用多個(gè)Condition實(shí)現(xiàn)通知所有線程
前面使用一個(gè)Condition對(duì)象來(lái)實(shí)現(xiàn)等待/通知模式,其實(shí)Condition對(duì)象也可以創(chuàng)建多個(gè)。那么一個(gè)Condition對(duì)象和多個(gè)Condition對(duì)象在使用上有什么區(qū)別呢?
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA時(shí)間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
condition.await();
System.out.println("end awaitA時(shí)間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB時(shí)間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
condition.await();
System.out.println("end awaitB時(shí)間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} public void signalAll() {
try {
lock.lock();
System.out.println("signalAll時(shí)間為"+System.currentTimeMillis());
condition.signalAll();
}finally {
lock.unlock();
}
}
} class MyThreadA extends Thread{
private MyService service; public MyThreadA(MyService service) {
this.service=service;
} @Override
public void run() {
service.awaitA();
}
} class MyThreadB extends Thread{
private MyService service; public MyThreadB(MyService service) {
this.service=service;
} @Override
public void run() {
service.awaitB();
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThreadA threadA=new MyThreadA(service);
threadA.setName("A");
threadA.start();
MyThreadB threadB=new MyThreadB(service);
threadB.setName("B");
threadB.start();
Thread.sleep(3000);
service.signalAll();
} }
運(yùn)行結(jié)果:
begin awaitA時(shí)間為1575600904529ThreadNameA
begin awaitB時(shí)間為1575600904545ThreadNameB
signalAll時(shí)間為1575600907537
end awaitA時(shí)間為1575600907537ThreadNameA
end awaitB時(shí)間為1575600907537ThreadNameB
6、使用多個(gè)Condition實(shí)現(xiàn)通知部分線程
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; 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時(shí)間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
conditionA.await();
System.out.println("end awaitA時(shí)間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB時(shí)間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
conditionB.await();
System.out.println("end awaitB時(shí)間為"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
} //通知A
public void signalAll_A() {
try {
lock.lock();
System.out.println("signalAll_A時(shí)間為"+System.currentTimeMillis()+"ThreadName="+Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
} //通知B
public void signalAll_B() {
try {
lock.lock();
System.out.println("signalAll_A時(shí)間為"+System.currentTimeMillis()+"ThreadName="+Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
}
} class MyThreadA extends Thread{
private MyService service; public MyThreadA(MyService service) {
this.service=service;
} @Override
public void run() {
service.awaitA();
}
} class MyThreadB extends Thread{
private MyService service; public MyThreadB(MyService service) {
this.service=service;
} @Override
public void run() {
service.awaitB();
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThreadA threadA=new MyThreadA(service);
threadA.setName("A");
threadA.start();
MyThreadB threadB=new MyThreadB(service);
threadB.setName("B");
threadB.start();
Thread.sleep(3000);
service.signalAll_A();
} }
運(yùn)行結(jié)果:
begin awaitA時(shí)間為1575601785167ThreadNameA
begin awaitB時(shí)間為1575601785167ThreadNameB
signalAll_A時(shí)間為1575601788181ThreadName=main
end awaitA時(shí)間為1575601788181ThreadNameA
上面的代碼實(shí)現(xiàn)通知部分線程,定義了兩個(gè)Condition,在測(cè)試類中只是喚醒了A,從輸出結(jié)果可以看出,線程A被喚醒了,線程B依然處于等待狀態(tài)
7、實(shí)現(xiàn)生產(chǎn)者/消費(fèi)者模式:一個(gè)生產(chǎn)者一個(gè)消費(fèi)者
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock 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(Exception 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(Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
} class MyThreadA extends Thread{
private MyService service; public MyThreadA(MyService service) {
this.service=service;
} @Override
public void run() {
for(int i=0;i<Integer.MAX_VALUE;i++) {
service.set();
} }
} class MyThreadB extends Thread{
private MyService service; public MyThreadB(MyService service) {
this.service=service;
} @Override
public void run() {
for(int i=0;i<Integer.MAX_VALUE;i++) {
service.get();
}
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThreadA a=new MyThreadA(service);
a.start();
MyThreadB b=new MyThreadB(service);
b.start();
} }
運(yùn)行結(jié)果:
上面代碼實(shí)現(xiàn)了生產(chǎn)者消費(fèi)者的功能,一個(gè)生產(chǎn)一個(gè)消費(fèi),如果hasValue=false相當(dāng)于生產(chǎn)者沒(méi)有生產(chǎn)產(chǎn)品,當(dāng)前沒(méi)有可消費(fèi)的產(chǎn)品,所以調(diào)用生產(chǎn)者生產(chǎn),當(dāng)hasValue=true說(shuō)明當(dāng)前有產(chǎn)品還沒(méi)有被消費(fèi),那么生產(chǎn)者應(yīng)該停止生產(chǎn),調(diào)用消費(fèi)者消費(fèi)
8、實(shí)現(xiàn)生產(chǎn)者/消費(fèi)者模式:多個(gè)生產(chǎn)者多個(gè)消費(fèi)者
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; class MyService{
private Lock 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.signal();
}catch(Exception 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.signal();
}catch(Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
} class MyThreadA extends Thread{
private MyService service; public MyThreadA(MyService service) {
this.service=service;
} @Override
public void run() {
for(int i=0;i<Integer.MAX_VALUE;i++) {
service.set();
} }
} class MyThreadB extends Thread{
private MyService service; public MyThreadB(MyService service) {
this.service=service;
} @Override
public void run() {
for(int i=0;i<Integer.MAX_VALUE;i++) {
service.get();
}
}
} public class LockTest { public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThreadA[] threadA=new MyThreadA[10];
MyThreadB[] threadB=new MyThreadB[10];
for(int i=0;i<10;i++) {
threadA[i]=new MyThreadA(service);
threadB[i]=new MyThreadB(service);
threadA[i].start();
threadB[i].start();
}
} }
運(yùn)行結(jié)果:
運(yùn)行程序后出現(xiàn)了假死,因?yàn)槌霈F(xiàn)了生產(chǎn)者釋放生產(chǎn)者或者消費(fèi)者釋放消費(fèi)者的情況,那么該如何解決這個(gè)問(wèn)題呢?在使用synchronized實(shí)現(xiàn)生產(chǎn)者消費(fèi)者的時(shí)候我們也遇到過(guò)這種情況,當(dāng)時(shí)是使用notifyAll()來(lái)解決這個(gè)問(wèn)題的,那么現(xiàn)在使用鎖我們則用signalAll()方法來(lái)解決死鎖問(wèn)題,將上述代碼中signal()方法改成signalAll()即可
修改后程序運(yùn)行結(jié)果如下
程序一直正常運(yùn)行,沒(méi)有出現(xiàn)死鎖情況
9、公平鎖和非公平鎖
公平與非公平鎖:鎖Lock分為“公平鎖”和“非公平鎖”,公平鎖表示線程獲取鎖的順序是按照線程加鎖的順序來(lái)分配的,即先來(lái)先得的FIFO先進(jìn)先出順序。而非公平鎖就是一種獲取鎖的搶占機(jī)制,是隨機(jī)獲得鎖的,和公平鎖不一樣的就是先來(lái)的不一定先得到鎖,這個(gè)方式可能造成某些線程一直拿不到鎖,結(jié)果也就是不公平的了。
創(chuàng)建公平鎖和非公平鎖ReentrantLock lock=new ReentrantLock(boolean a),創(chuàng)建鎖時(shí)如果a為true的話,則創(chuàng)建的是公平鎖,如果a為false的話,則創(chuàng)建的是非公平鎖
公平鎖
import java.util.concurrent.locks.ReentrantLock;
class Service{
private ReentrantLock lock;
public Service(boolean isFair) {
lock=new ReentrantLock(isFair);
}
public void serviceMethod() {
try {
lock.lock();
System.out.println("ThreadName="+Thread.currentThread().getName()+"獲得鎖定");
}catch(Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class LockTest {
public static void main(String[] args) throws InterruptedException {
final Service service=new Service(true);
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("★線程"+Thread.currentThread().getName()+"運(yùn)行了");
service.serviceMethod();
}
};
Thread[] threadArray=new Thread[10];
for(int i=0;i<10;i++) {
threadArray[i]=new Thread(runnable);
}
for(int i=0;i<10;i++) {
threadArray[i].start();
}
}
}
運(yùn)行結(jié)果:
★線程Thread-2運(yùn)行了
★線程Thread-3運(yùn)行了
★線程Thread-0運(yùn)行了
★線程Thread-9運(yùn)行了
★線程Thread-4運(yùn)行了
★線程Thread-8運(yùn)行了
★線程Thread-5運(yùn)行了
★線程Thread-1運(yùn)行了
★線程Thread-6運(yùn)行了
★線程Thread-7運(yùn)行了
ThreadName=Thread-2獲得鎖定
ThreadName=Thread-6獲得鎖定
ThreadName=Thread-1獲得鎖定
ThreadName=Thread-8獲得鎖定
ThreadName=Thread-0獲得鎖定
ThreadName=Thread-7獲得鎖定
ThreadName=Thread-5獲得鎖定
ThreadName=Thread-3獲得鎖定
ThreadName=Thread-9獲得鎖定
ThreadName=Thread-4獲得鎖定
結(jié)果顯示輸出基本是呈有序的狀態(tài),這就是公平鎖的特點(diǎn)
非公平鎖
import java.util.concurrent.locks.ReentrantLock;
class Service{
private ReentrantLock lock;
public Service(boolean isFair) {
lock=new ReentrantLock(isFair);
}
public void serviceMethod() {
try {
lock.lock();
System.out.println("ThreadName="+Thread.currentThread().getName()+"獲得鎖定");
}catch(Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class LockTest {
public static void main(String[] args) throws InterruptedException {
final Service service=new Service(false);
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("★線程"+Thread.currentThread().getName()+"運(yùn)行了");
service.serviceMethod();
}
};
Thread[] threadArray=new Thread[10];
for(int i=0;i<10;i++) {
threadArray[i]=new Thread(runnable);
}
for(int i=0;i<10;i++) {
threadArray[i].start();
}
}
}
運(yùn)行結(jié)果:
★線程Thread-2運(yùn)行了
★線程Thread-9運(yùn)行了
★線程Thread-7運(yùn)行了
★線程Thread-0運(yùn)行了
★線程Thread-3運(yùn)行了
★線程Thread-1運(yùn)行了
★線程Thread-6運(yùn)行了
★線程Thread-5運(yùn)行了
★線程Thread-4運(yùn)行了
ThreadName=Thread-1獲得鎖定
★線程Thread-8運(yùn)行了
ThreadName=Thread-8獲得鎖定
ThreadName=Thread-2獲得鎖定
ThreadName=Thread-7獲得鎖定
ThreadName=Thread-5獲得鎖定
ThreadName=Thread-3獲得鎖定
ThreadName=Thread-4獲得鎖定
ThreadName=Thread-9獲得鎖定
ThreadName=Thread-0獲得鎖定
ThreadName=Thread-6獲得鎖定
非公平鎖的運(yùn)行結(jié)果基本上是亂序的,說(shuō)明先start()啟動(dòng)的線程不代表先獲得鎖
10、ReentranLock方法概述:
(1)、int getHoldCount()
getHoldCount()的作用是查詢當(dāng)前線程保持此鎖定的個(gè)數(shù),也就是調(diào)用lock()方法的次數(shù)。
(2)、int getQueueLength()
getQueueLength()的作用是返回正等待獲取此鎖定的線程估計(jì)數(shù),比如有5個(gè)線程,1個(gè)線程首先執(zhí)行awai()方法,那么在調(diào)用getQueueLength()方法后返回值是4,說(shuō)明有4個(gè)線程同時(shí)在等待lock的釋放。
(3)、int getWaitQueueLength(Condition condition)
getWaitQueueLength(Condition condition)的作用是返回等待與此鎖定相關(guān)的給定條件Condition的線程估計(jì)數(shù),比如有5個(gè)線程,每個(gè)線程都執(zhí)行了同一個(gè)condition對(duì)象的await()方法,則調(diào)用getWaitQueueLength(Condition condition)方法時(shí)返回的int值是5。
(4)、boolean hasQueuedThread(Thread thread)
hasQueuedThread(Thread thread)的作用是查詢指定的線程是否正在等待獲取此鎖定
hasQueuedThreads()的作用是查詢是否有線程正在等待獲取此鎖定。
(5)、boolean hasWaiters(Condition condition)
hasWaiters(Condition condition)的作用是查詢是否有線程正在等待與此鎖定有關(guān)的condition條件。
(6)、boolean isFair()
isFair()的作用是判斷是不是公平鎖
(7)、boolean isHeldByCurrentThread()
isHeldByCurrentThread的作用是查詢當(dāng)前線程是否保持此鎖定
(8)、boolean isLocked()
isLocked()的作用是查詢此鎖定是否由任意的線程保持
ReentrantReadWriteLock
類ReentrantLock具有完全互斥排他的效果,即同一時(shí)間只有一個(gè)線程在執(zhí)行ReentrantLock.lock()方法后面的任務(wù)。這樣做雖然保證了實(shí)例變量的線程安全性,但效率卻是非常低下的。所以在JDK中提供了一種讀寫鎖ReentrantReadWriteLock類,使用它可以加快運(yùn)行效率,在某些不需要操作實(shí)例變量的方法中,完全可以使用讀寫鎖ReentrantReadWriteLock 來(lái)提升該方法的代碼運(yùn)行速度。
讀寫鎖表示也有兩個(gè)鎖,一個(gè)是讀操作相關(guān)的鎖,也稱為共享鎖;另一個(gè)是寫操作相關(guān)的鎖,也叫排他鎖。也就是多個(gè)讀鎖之間不互斥,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥。在沒(méi)有線程Thread進(jìn)行寫入操作時(shí),進(jìn)行讀取操作的多個(gè)Thread都可以獲取讀鎖,而進(jìn)行寫入操作的Thread只有在獲取寫鎖后才能進(jìn)行寫入操作。即多個(gè)Thread可以同時(shí)進(jìn)行讀取操作,但是同一時(shí)刻只允許一個(gè)Thread進(jìn)行寫入操作。
一、ReentrantReadWriteLock讀讀共享
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Service{
private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
public void read() {
try {
try {
lock.readLock().lock();
System.out.println("獲取讀鎖"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.readLock().unlock();
}
}catch(Exception e) {
e.printStackTrace();
}
}
}
class MyThreadA extends Thread{
private Service service;
public MyThreadA(Service service) {
this.service=service;
}
@Override
public void run() {
service.read();
}
}
class MyThreadB extends Thread{
private Service service;
public MyThreadB(Service service) {
this.service=service;
}
@Override
public void run() {
service.read();
}
}
public class LockTest {
public static void main(String[] args) throws InterruptedException {
Service service=new Service();
MyThreadA a=new MyThreadA(service);
a.setName("A");
MyThreadB b=new MyThreadB(service);
b.setName("B");
a.start();
b.start();
}
}
運(yùn)行結(jié)果:
獲取讀鎖A 1575611161158
獲取讀鎖B 1575611161158
從輸出結(jié)果打印的時(shí)間來(lái)看,兩個(gè)線程幾乎同時(shí)進(jìn)入lock()方法后面的代碼。說(shuō)明在此使用了lock.readLock()讀鎖可以提高程序運(yùn)行效率,允許多個(gè)線程同時(shí)執(zhí)行l(wèi)ock()方法后面的代碼。
二、ReentrantReadWriteLock寫寫互斥
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Service{
private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
public void write() {
try {
try {
lock.writeLock().lock();
System.out.println("獲取寫鎖"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.writeLock().unlock();
}
}catch(Exception e) {
e.printStackTrace();
}
}
}
class MyThreadA extends Thread{
private Service service;
public MyThreadA(Service service) {
this.service=service;
}
@Override
public void run() {
service.write();
}
}
class MyThreadB extends Thread{
private Service service;
public MyThreadB(Service service) {
this.service=service;
}
@Override
public void run() {
service.write();
}
}
public class LockTest {
public static void main(String[] args) throws InterruptedException {
Service service=new Service();
MyThreadA a=new MyThreadA(service);
a.setName("A");
MyThreadB b=new MyThreadB(service);
b.setName("B");
a.start();
b.start();
}
}
運(yùn)行結(jié)果:
獲取寫鎖B 1575611458260
獲取寫鎖A 1575611468273
結(jié)果顯示寫鎖的效果是同一時(shí)間只允許一個(gè)線程執(zhí)行l(wèi)ock()后面的代碼
三、ReentrantReadWriteLock讀寫互斥
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Service{
private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
public void read() {
try {
try {
lock.readLock().lock();
System.out.println("獲取讀鎖"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.readLock().unlock();
}
}catch(Exception e) {
e.printStackTrace();
}
}
public void write() {
try {
try {
lock.writeLock().lock();
System.out.println("獲取寫鎖"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.writeLock().unlock();
}
}catch(Exception e) {
e.printStackTrace();
}
}
}
class MyThreadA extends Thread{
private Service service;
public MyThreadA(Service service) {
this.service=service;
}
@Override
public void run() {
service.read();
}
}
class MyThreadB extends Thread{
private Service service;
public MyThreadB(Service service) {
this.service=service;
}
@Override
public void run() {
service.write();
}
}
public class LockTest {
public static void main(String[] args) throws InterruptedException {
Service service=new Service();
MyThreadA a=new MyThreadA(service);
a.setName("A");
MyThreadB b=new MyThreadB(service);
b.setName("B");
a.start();
b.start();
}
}
運(yùn)行結(jié)果:
獲取讀鎖A 1575611689661
獲取寫鎖B 1575611699665
從讀寫的時(shí)間上可以看出讀寫的操作時(shí)互斥的
總結(jié)
以上是生活随笔為你收集整理的Java多线程——锁的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java第三章 类和对象3.1+3.2
- 下一篇: java第九章IO流与文件操作