谈谈java并发锁(重入锁、读写锁、公平锁)
目錄
重入鎖
簡單重入鎖
重入鎖的等待通知(Condition)
多Condition
公平鎖和非公平鎖
讀寫鎖ReentrantReadWriteLock
鎖優化總結:
重入鎖和讀寫鎖,他們具有比synchronized更為強大的功能,并且有嗅探鎖定、多路分支等功能。
重入鎖
在需要進行同步的代碼部分加上鎖定,但不要忘記最后一定要釋放鎖定,不然會造成鎖永遠無法釋放,其他線程永遠進不來的結果。
簡單重入鎖
//一定要在finally中解鎖 //感覺跟synchronized沒啥區別,是對象鎖。也許性能比較好一些 public class UseReentrantLock {//private Lock lock = new ReentrantLock();//定義一個鎖沖突public void method1(){Lock lock = new ReentrantLock();//定義兩個鎖不沖突try {lock.lock();System.out.println("當前線程:" + Thread.currentThread().getName() + "進入method1..");Thread.sleep(1000);System.out.println("當前線程:" + Thread.currentThread().getName() + "退出method1..");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void method2(){Lock lock = new ReentrantLock();//定義兩個鎖不沖突try {lock.lock();System.out.println("當前線程:" + Thread.currentThread().getName() + "進入method2..");Thread.sleep(2000);System.out.println("當前線程:" + Thread.currentThread().getName() + "退出method2..");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) {final UseReentrantLock ur = new UseReentrantLock();final UseReentrantLock ur2 = new UseReentrantLock();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {ur.method1();}}, "t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {ur2.method2();}}, "t2");t1.start();t2.start();try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//System.out.println(ur.lock.getQueueLength());} }重入鎖的等待通知(Condition)
就像synchronized,wait()、notify()、notifyAll()。
同樣,在使用Lock的時候,可以使用一個新的等待/通知的類,它就是Condition。這個Condition一定是針對具體某一把鎖的。也就是在只有鎖的基礎之上才會產生Condition。
public class UseCondition {private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();public void method1(){try {lock.lock();System.out.println("當前線程:" + Thread.currentThread().getName() + "進入等待狀態..");Thread.sleep(3000);System.out.println("當前線程:" + Thread.currentThread().getName() + "釋放鎖..");condition.await(); // Object waitSystem.out.println("當前線程:" + Thread.currentThread().getName() +"繼續執行...");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void method2(){try {lock.lock();System.out.println("當前線程:" + Thread.currentThread().getName() + "進入..");Thread.sleep(3000);System.out.println("當前線程:" + Thread.currentThread().getName() + "發出喚醒..");condition.signal(); //Object有notifyAll,同理這里也有signalAll()方法} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) {final UseCondition uc = new UseCondition();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {uc.method1();}}, "t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {uc.method2();}}, "t2");t1.start();t2.start();} }多Condition
我們可以通過一個Lock對象產生多個Condition進行多線程間的交互,非常的靈活??梢允沟貌糠中枰獑拘训木€程喚醒,其他線程則繼續等待通知。
//兩個Condition是獨立的!!! public class UseManyCondition {private ReentrantLock lock = new ReentrantLock();private Condition c1 = lock.newCondition();private Condition c2 = lock.newCondition();public void m1(){try {lock.lock();System.out.println("當前線程:" +Thread.currentThread().getName() + "進入方法m1等待..");c1.await();System.out.println("當前線程:" +Thread.currentThread().getName() + "方法m1繼續..");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void m2(){try {lock.lock();System.out.println("當前線程:" +Thread.currentThread().getName() + "進入方法m2等待..");c1.await();System.out.println("當前線程:" +Thread.currentThread().getName() + "方法m2繼續..");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void m3(){try {lock.lock();System.out.println("當前線程:" +Thread.currentThread().getName() + "進入方法m3等待..");c2.await();System.out.println("當前線程:" +Thread.currentThread().getName() + "方法m3繼續..");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void m4(){try {lock.lock();System.out.println("當前線程:" +Thread.currentThread().getName() + "喚醒..");c1.signalAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void m5(){try {lock.lock();System.out.println("當前線程:" +Thread.currentThread().getName() + "喚醒..");c2.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) {final UseManyCondition umc = new UseManyCondition();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {umc.m1();}},"t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {umc.m2();}},"t2");Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {umc.m3();}},"t3");Thread t4 = new Thread(new Runnable() {@Overridepublic void run() {umc.m4();}},"t4");Thread t5 = new Thread(new Runnable() {@Overridepublic void run() {umc.m5();}},"t5");t1.start(); // c1t2.start(); // c1t3.start(); // c2try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}t4.start(); // c1try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}t5.start(); // c2} }公平鎖和非公平鎖
默認是非公平,非公平鎖效率比較高。
Lock?lock = new ReentrantLock(boolean isFair);//可傳參,默認是false非公平鎖。
lock用法:
tryLock():嘗試獲得鎖,獲得結果用true/false返回。
tryLock():在給定的時間內嘗試獲得鎖,獲得結果用true/false返回。
isFair():是否是公平鎖。
isLocked():是否鎖定。
getHoldCount():查詢當前線程保持此鎖的個數,也就是調用lock()次數。
lockInterruptibly():優先響應中斷的鎖。
getQueueLength():返回正在等待獲取此鎖的線程數。
getWaitQueueLength():返回等待與鎖定相關的給定條件Condition的線程數。
hasQueuedThread(Thread thread):查詢指定的線程是否在等待此鎖。
hasQueuedThreads():查詢是否有線程正在等待此鎖。
hasWaiters():查詢是否有線程正在等待與此鎖定有關的condition條件。
讀寫鎖ReentrantReadWriteLock
讀寫鎖ReentrantReadWriteLock,其核心就是實現讀寫分離的鎖。在高并發訪問下,尤其是讀多寫少的情況下,性能要遠高于重入鎖。
之前的synchronized、ReentrantLock,我們知道,同一時間內只能有一個線程進行訪問被鎖定的代碼,那么讀寫鎖則不同,其本質是分成兩個鎖,即讀鎖、寫鎖。在讀鎖下,多個線程可以并發的進行訪問,但是在寫鎖的時候,只能一個個的順序訪問。
口訣:讀讀共享,寫寫互斥,讀寫互斥。
public class UseReentrantReadWriteLock {private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();private ReadLock readLock = rwLock.readLock();private WriteLock writeLock = rwLock.writeLock();public void read(){try {readLock.lock();System.out.println("當前線程:" + Thread.currentThread().getName() + "進入...");Thread.sleep(3000);System.out.println("當前線程:" + Thread.currentThread().getName() + "退出...");} catch (Exception e) {e.printStackTrace();} finally {readLock.unlock();}}public void write(){try {writeLock.lock();System.out.println("當前線程:" + Thread.currentThread().getName() + "進入...");Thread.sleep(3000);System.out.println("當前線程:" + Thread.currentThread().getName() + "退出...");} catch (Exception e) {e.printStackTrace();} finally {writeLock.unlock();}}public static void main(String[] args) {final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {urrw.read();}}, "t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {urrw.read();}}, "t2");Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {urrw.write();}}, "t3");Thread t4 = new Thread(new Runnable() {@Overridepublic void run() {urrw.write();}}, "t4"); // t1.start();// R // t2.start();// R// t1.start(); // R // t3.start(); // Wt3.start();// Wt4.start();// W} }鎖優化總結:
1.避免死鎖。
2.減小鎖的持有時間。
3.減小鎖的粒度。
4.鎖的分離(讀寫鎖)。
5.盡量使用無鎖的操作,如原子操作(Atomic系列類),volatile關鍵字。
總結
以上是生活随笔為你收集整理的谈谈java并发锁(重入锁、读写锁、公平锁)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 谈谈java的线程池(创建、机制)
- 下一篇: 谈谈java的bio、nio、aio模型