接下來介紹比synchronized功能上更豐富的關(guān)鍵字:重入鎖
-
靈活性:
public class ReentrantLockTest implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int flag = 0; @Override public void run() { for (int i = 0; i < 10000000; i++) { lock.lock(); try { flag++; } finally { lock.unlock(); } } } public static void main(String args[]) throws InterruptedException { ReentrantLockTest test = new ReentrantLockTest(); Thread first = new Thread(test); Thread second = new Thread(test); first.start(); second.start(); first.join(); second.join(); System.out.println(flag); } }
在
lock.lock();
這里恒序,通過重入鎖保護臨界區(qū)安全,以免發(fā)生線程安全問題。在
lock.unlock();
這里涧至,必須手動指示釋放鎖的操作氏仗,否則其他線程將無法獲得。在這段代碼里,我們能見到重入鎖靈活的特點嫩絮。但為什么叫“重入”呢婆殿?
看下段代碼:
@Override public void run() { for (int i = 0; i < 10000000; i++) { lock.lock(); lock.lock(); try { flag++; } finally { lock.unlock(); lock.unlock(); } } }
因為該鎖能反復進進出出诈乒。但要注意一下:
在上段代碼中,鎖是可以重復獲取的婆芦。如果不允許怕磨,則該線程在第二次獲取鎖時會和自己產(chǎn)生死鎖問題。同時也要注意寞缝,線程獲取多少次鎖就要釋放多少此鎖癌压。當獲取鎖的次數(shù)大于釋放鎖的次數(shù)、相當于該線程還持有鎖荆陆。當獲取鎖的次數(shù)少于釋放鎖的次數(shù)滩届、則會得到一個
java.lang.IllegalMonitorStateException
異常。 -
中斷響應:
二話不說貼代碼:
public class ReentrantLockTest implements Runnable{ public static ReentrantLock producer = new ReentrantLock(); public static ReentrantLock consumer = new ReentrantLock(); public int flag = 0; public ReentrantLockTest(int flag){ this.flag = flag; } @Override public void run() { try { if (flag == 0) { producer.lockInterruptibly(); try { Thread.sleep(500); } catch (InterruptedException e) { } consumer.lockInterruptibly(); System.out.println(Thread.currentThread().getName() + "完成工作"); } else { consumer.lockInterruptibly(); try { Thread.sleep(500); } catch (InterruptedException e) { } producer.lockInterruptibly(); System.out.println(Thread.currentThread().getName() + "完成工作"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if (producer.isHeldByCurrentThread()) { producer.unlock(); } if (consumer.isHeldByCurrentThread()) { consumer.unlock(); } System.out.println(Thread.currentThread().getName() + ": 線程退出"); } } public static void main(String args[]) throws InterruptedException { ReentrantLockTest producerThread = new ReentrantLockTest(1); ReentrantLockTest consumerThread = new ReentrantLockTest(0); Thread first = new Thread(producerThread); Thread second = new Thread(consumerThread); first.setName("producer"); second.setName("consumer"); first.start(); second.start(); Thread.sleep(1000); second.interrupt(); } }
這是一段容易造成死鎖的代碼被啼,具體原因大家應該懂帜消。當執(zhí)行到
second.interrupt();
時,second線程在等待鎖時被中斷浓体,故second線程會放棄對鎖的申請泡挺、并對已持有資源進行釋放。first線程則能夠正常獲取所等待的鎖并繼續(xù)執(zhí)行下去命浴。結(jié)果如下:
producer完成工作 java.lang.InterruptedException consumer: 線程退出 producer: 線程退出 at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335) at blog.ReentrantLockTest.run(ReentrantLockTest.java:22) at java.lang.Thread.run(Thread.java:748)
真正完成工作的只有producer線程娄猫。
-
限時等待:
除了用中斷避免死鎖問題外,還可以用限時等待鎖來避免生闲。限時等待鎖有點像是系統(tǒng)自動完成線程中斷的感覺媳溺。先展示下限時等待鎖的使用:
public class showWait implements Runnable { public static ReentrantLock lock = new ReentrantLock(); @Override public void run() { try { if (lock.tryLock(5, TimeUnit.SECONDS)) { Thread.sleep(6000); } else { System.out.println(Thread.currentThread().getName() + " get lock failed"); } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String args[]) { showWait test = new showWait(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.setName("producer"); t2.setName("consumer"); t1.start(); t2.start(); } }
上述代碼展示了
lock.tryLock(5,TimeUnit.SECONDS);
的使用碍讯,在這里悬蔽,該方法接收兩個參數(shù),分別是時長和計時單位捉兴。該方法也可以不帶參數(shù)蝎困,當不帶參數(shù)時录语,當前線程會嘗試獲取鎖,如果鎖未被其他線程占有則會申請成功并立即返回true禾乘。如果鎖被其他線程占用則立即返回false澎埠。這種方法不會引起線程等待,所以不會產(chǎn)生死鎖問題盖袭。
-
公平鎖:
在多大數(shù)情況下失暂,鎖的申請都是非公平性的,有時會造成線程饑餓問題鳄虱。當我們使用synchronized時產(chǎn)生的鎖是非公平性的弟塞,但我們使用ReentrantLock時可以通過構(gòu)造函數(shù)進行指定其公平性。
public ReentrantLock(boolean fair)
當參數(shù)為true時為公平鎖拙已,默認為非公平鎖决记。公平鎖看起來挺優(yōu)美的,但其必然要維護一個等待隊列倍踪,其性能必然會降低
-
整理:
- lock()
- lockInterruptibly()
- tryLock()
- tryLock(long time,TimeUnit unit)
- unlock()
大家回顧下這幾個方法吧系宫。