標籤:lam 指定 對比 自己 手工 應該 read clock 非公平鎖
java高並發主要有三塊知識點:synchronizer:同步器,在多個線程之間互相之間怎麼進行通訊,同步等;同步容器:jdk提供了同步性的容器,比如concurrentMap,concurrentList,BlockQueen等;ThreadPool:線程池,executor,java在前兩個的基礎之上提供的線程池,很多實際中的問題可以著手解決了。 一、ReentrantLock jdk裡面提供了一個新的鎖,是手工鎖,它是用來替代synchronized的,叫ReentrantLock,重入鎖,其實synchronized也是可重新進入的,但是這把鎖是和synchronized是有區別的,ReentrantLock是用新的同步方法寫的時候經常用的一個工具;複習之前講的synchronized同步:
/** * reentrantlock用於替代synchronized * 本例中由於m1鎖定this,只有m1執行完畢的時候,m2才能執行 * 這裡是複習synchronized最原始的語義 * @author mashibing */package yxxy.c_020;import java.util.concurrent.TimeUnit;public class ReentrantLock1 { synchronized void m1() { for(int i=0; i<10; i++) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i); } } synchronized void m2() { System.out.println("m2 ..."); } public static void main(String[] args) { ReentrantLock1 rl = new ReentrantLock1(); new Thread(rl::m1).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(rl::m2).start(); }}View Code
二、使用ReentrantLock完成同樣功能
/** * reentrantlock用於替代synchronized * 使用reentrantlock可以完成同樣的功能 * 需要注意的是,必須要必須要必須要手動釋放鎖(重要的事情說三遍) * 使用syn鎖定的話如果遇到異常,jvm會自動釋放鎖,但是lock必須手動釋放鎖,因此經常在finally中進行鎖的釋放 * @author mashibing */package yxxy.c_020;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ReentrantLock2 { Lock lock = new ReentrantLock(); void m1() { try { lock.lock(); //synchronized(this) for (int i = 0; i < 10; i++) { TimeUnit.SECONDS.sleep(1); System.out.println(i); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } void m2() { lock.lock(); System.out.println("m2 ..."); lock.unlock(); } public static void main(String[] args) { ReentrantLock2 rl = new ReentrantLock2(); new Thread(rl::m1).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(rl::m2).start(); }}
三、RenntrantLock的tryLock:
/** * 使用reentrantlock可以進行“嘗試鎖定”tryLock,這樣無法鎖定,或者在指定時間內無法鎖定,線程可以決定是否繼續等待 * @author mashibing */package yxxy.c_020;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ReentrantLock3 { Lock lock = new ReentrantLock(); void m1() { try { lock.lock(); for (int i = 0; i < 10; i++) { TimeUnit.SECONDS.sleep(1); System.out.println(i); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } /** * 使用tryLock進行嘗試鎖定,不管鎖定與否,方法都將繼續執行 * 可以根據tryLock的傳回值來判定是否鎖定 * 也可以指定tryLock的時間,由於tryLock(time)拋出異常,所以要注意unclock的處理,必須放到finally中 */ void m2() { /* boolean locked = lock.tryLock(); System.out.println("m2 ..." + locked); if(locked) lock.unlock(); */ boolean locked = false; try { locked = lock.tryLock(5, TimeUnit.SECONDS); System.out.println("m2 ..." + locked); } catch (InterruptedException e) { e.printStackTrace(); } finally { if(locked) lock.unlock(); } } public static void main(String[] args) { ReentrantLock3 rl = new ReentrantLock3(); new Thread(rl::m1).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(rl::m2).start(); }}View Code
console:
012345m2 ...false6789
View Code
四、ReentrantLock的lockInterruptibly方法:
/** * 使用ReentrantLock還可以調用lockInterruptibly方法,可以對線程interrupt方法做出響應, * 在一個線程等待鎖的過程中,可以被打斷 * * @author mashibing */package yxxy.c_020;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;import java.util.function.Function;public class ReentrantLock4 { public static void main(String[] args) { Lock lock = new ReentrantLock(); Thread t1 = new Thread(()->{ try { lock.lock(); System.out.println("t1 start"); TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); System.out.println("t1 end"); } catch (InterruptedException e) { System.out.println("interrupted!"); } finally { lock.unlock(); } }); t1.start(); Thread t2 = new Thread(()->{ try { //lock.lock(); lock.lockInterruptibly(); //可以對interrupt()方法做出響應 System.out.println("t2 start"); } catch (InterruptedException e) { System.out.println("interrupted!"); } finally { lock.unlock(); } }); t2.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } t2.interrupt(); //打斷線程2的等待 }}
console:
t1 startinterrupted!Exception in thread "Thread-1" java.lang.IllegalMonitorStateException at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151) at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261) at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457) at yxxy.c_020.ReentrantLock4.lambda$1(ReentrantLock4.java:42) at java.lang.Thread.run(Thread.java:745)
t1線程牢牢的拿到鎖之後,一直sleep不會釋放,如果t2線程中的run方法使用lock.lock(),那麼t2線程就會一直傻傻的等著這把鎖,不能被其他線程打斷;
而使用lockInterruptibly()方法是可以被打斷的,主線程main調用t2.interrupt()來打斷t2,告訴他是不會拿到這把鎖的,別等了;
報錯是因為lock.unlock()這個方法報錯的,因為都沒有拿到鎖,無法unlock();是代碼的問題,應該判斷有鎖,已經鎖定的情況下才lock.unlock();
五、ReentrantLock還可以指定為公平鎖
預設的synchronized全都是不公平鎖;
什麼是公平鎖,什麼是不公平鎖,假設很多個線程訪問同一份資源的時候都要鎖定,其中某一個線程A如果拿到了,他佔有這把鎖之後,其他人是都訪問不了的,都得在那等著;什麼時候一旦線程A釋放了這把鎖,那麼剩下的線程中哪個線程得到這把鎖,這事說不定,這個要看線程調度器自己去選哪個了,所以這叫競爭鎖;也就是說等待的線程裡面是沒有公平性可言的;不過這種效率比較高,線程調度器不用計算到底哪個線程等的時間更長,所以預設的synchronized是非公平鎖; 公平鎖就是,誰等的時間長讓誰得到那把鎖。
/** * ReentrantLock還可以指定為公平鎖 * * @author mashibing */package yxxy.c_020;import java.util.concurrent.locks.ReentrantLock;public class ReentrantLock5 extends Thread { private static ReentrantLock lock = new ReentrantLock(); //參數為true表示為公平鎖,請對比輸出結果 public void run() { for(int i=0; i<100; i++) { lock.lock(); try{ System.out.println(Thread.currentThread().getName()+"獲得鎖"); }finally{ lock.unlock(); } } } public static void main(String[] args) { ReentrantLock5 rl=new ReentrantLock5(); Thread th1=new Thread(rl); Thread th2=new Thread(rl); th1.start(); th2.start(); }}
六、
java高並發編程(三)