標籤:err 重複 art 執行 main int 取消 使用 print
1.定義
重入鎖ReentrantLock,支援重入的鎖,表示一個線程對資源的重複加鎖。
2.底層實現
每個鎖關聯一個線程持有人和計數器,當計數器為0時表示該鎖沒有被任何線程持有,那麼任何線程都可能獲得該鎖而調用相應的方法;成功後,JVM會記下鎖的持有線程,並且將計數器置為1;此時其它線程請求該鎖,則必須等待;而該持有鎖的線程如果再次請求這個鎖,就可以再次拿到這個鎖,同時計數器會遞增;當線程退出同步代碼塊時,計數器會遞減,如果計數器為0,則釋放該鎖。
3.使用範例
eg:
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockTest implements Runnable{ public static int i = 100; public static ReentrantLock rl = new ReentrantLock(); @Override public void run() { while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } rl.lock(); if(i>0){ System.out.println(Thread.currentThread().getName() + " : " + i--); } rl.unlock(); } } public static void main(String[] args) { ReentrantLockTest rlt = new ReentrantLockTest(); Thread t1 = new Thread(rlt); Thread t2 = new Thread(rlt); t1.start(); t2.start(); }}
中斷響應
synchronized:線程等待鎖,要麼獲得鎖繼續執行,要麼保持等待。
ReentrantLock:等待鎖的過程中,可以根據需求取消對鎖的請求。
eg:
import java.util.concurrent.locks.ReentrantLock;public class IntLock implements Runnable { int lock; public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); public IntLock(int lock){ this.lock = lock; } @Override public void run() { try{ if(lock == 1){ try{ lock1.lockInterruptibly(); // 如果當前線程未被中斷,則擷取鎖。 }catch(Exception e){ e.printStackTrace(); } lock2.lockInterruptibly(); System.out.println(Thread.currentThread().getId()+"執行完畢"); }else{ lock2.lockInterruptibly(); try{ Thread.sleep(1000); }catch(Exception e){ } lock1.lockInterruptibly(); } }catch(Exception e){ e.printStackTrace(); }finally{ if(lock1.isHeldByCurrentThread()){ //查詢當前線程是否保持此鎖 lock1.unlock(); } if(lock2.isHeldByCurrentThread()){ //查詢當前線程是否保持此鎖 lock2.unlock(); } System.out.println(Thread.currentThread().getId()+":線程退出"); } } public static void main(String[] args) throws InterruptedException { IntLock l1 = new IntLock(1); IntLock l2 = new IntLock(2); Thread t1 = new Thread(l1); Thread t2 = new Thread(l2); t1.start(); t2.start(); Thread.sleep(2000); t2.interrupt(); //中斷線程,不中斷,則產生死結 }}
鎖申請等待限時
如果鎖在給定等待時間內沒有被另一個線程保持,且當前線程未被中斷,則擷取該鎖。
import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantLock;public class TimeLock implements Runnable { public static ReentrantLock rl = new ReentrantLock(); @Override public void run() { try { if(rl.tryLock(5, TimeUnit.SECONDS)){ Thread.sleep(6000); System.out.println(Thread.currentThread().getId()+"執行完畢"); }else{ System.out.println(Thread.currentThread().getId()+"get lock failed"); } } catch (InterruptedException e) { e.printStackTrace(); }finally{ if(rl.isHeldByCurrentThread())rl.unlock(); } } public static void main(String[] args) { TimeLock tl = new TimeLock(); Thread t1 = new Thread(tl); Thread t2 = new Thread(tl); t1.start(); t2.start(); }}
公平鎖
在ReentrantLock中很明顯可以看到其中同步包括兩種,分別是公平的FairSync和非公平的NonfairSync。公平鎖的作用就是嚴格按照線程啟動的順序來執行的,不允許其他線程插隊執行的;而非公平鎖是允許插隊的。預設情況下ReentrantLock是通過非公平鎖來進行同步的,包括synchronized關鍵字都是如此,因為這樣效能會更好。
public ReentrantLock(boolean fair) //如果此鎖應該使用公平的排序策略,則該參數為 true
import java.util.concurrent.locks.ReentrantLock;public class FairLock implements Runnable{ public static ReentrantLock rl = new ReentrantLock(true); @Override public void run() { while(true){ try { rl.lock(); System.out.println(Thread.currentThread().getName()+" : get lock"); } catch (Exception e) { }finally{ rl.unlock(); } } } public static void main(String[] args) { FairLock fl = new FairLock(); Thread t1 = new Thread(fl,"Thread_One"); Thread t2 = new Thread(fl,"Thread_Two"); t1.start(); t2.start(); }}
在重入鎖的實現中,包含3個要素
1.原子狀態,使用CAS操作來儲存當前鎖的狀態,判斷鎖是否被別的線程持有
2.等待隊列,所有沒有請求到鎖的線程,會進入到隊列進行等待,待有線程釋放鎖後,系統能從等待隊列喚醒一個線程繼續工作。
3.阻塞原語park()和unpark(),用來掛起和恢複線程,沒有得到鎖的線程會被掛起
java多線程---重入鎖ReentrantLock