前言:在上面的博客說了synchronized的一些用法,下面咱們再來看看lock,這個出現頻率也是很是高的一個。html
前面說了synchronized有鎖對象和鎖類對象,當某個線程獲取鎖其餘線程必須等待執行完畢纔可繼續進行,好比線程A先獲取鎖,可是出現異常致使的後果就是線程B沒法獲取鎖,會出現死鎖的狀況(http://www.cnblogs.com/LipeiNet/p/6475851.html),那麼咱們一塊兒看看Lock是如何解決的。lock有4種方式來獲取鎖緩存
1:lock.lock() 若是獲取了鎖當即返回,若是別的線程持有鎖,當前線程則一直處於休眠狀態,直到獲取鎖。此種模式和synchronized同樣可是不會出現死鎖併發
public class Lock1 { static int value = 0; static Lock lock = new ReentrantLock(); static class Task1 implements Runnable { public void run() { System.out.println("線程" + Thread.currentThread().getName() + "開始執行"); lock.lock(); try { for (int i = 0; i < 1000000; i++) { value++; } System.out.println(value); } finally { lock.unlock(); } } } static class Task2 implements Runnable { public void run() { System.out.println("線程" + Thread.currentThread().getName() + "開始執行"); lock.lock(); try { for (int i = 0; i < 1000000; i++) { value++; } System.out.println(value); } finally { lock.unlock(); } } } public static void main(String[] args) { ExecutorService service= Executors.newCachedThreadPool(); service.execute(new Task1()); service.execute(new Task2()); service.shutdown(); } }
輸出結果很明顯其中一個value是1000000,一個是2000000,效果和synchronized是同樣的。可是若是咱們去掉lock之後的結果呢,很明顯會錯,以下圖這樣this
爲啥會出現這樣狀況呢,是因爲cpu速度極快,每次處理完畢以後並無當即把數值放入Java內存中,而是放在寫緩存區,而後由寫緩存區同步到Java內存中,這樣同樣,若是線程1計算結果是2,可是仍是到內存中,致使線程2覺得value值仍是1因此會重複計算,還有從結果咱們也能夠看出value值並非100000說明2個線程是同步執行的。spa
2:lock.tryLock();線程
這個方法和synchronized有所不一樣,synchronized和lock都會等待直到獲取鎖。若是獲取了鎖當即返回true,若是別的線程正持有鎖,當即返回false;固然咱們能夠利用while循環一直等待,直到獲取鎖而後進行。代碼以下3d
public class Lock2 { public static void main(String[] args) { final Lock lock = new ReentrantLock(); Thread t1 = new Thread(new Runnable() { public void run() { String tName = Thread.currentThread().getName(); while (!lock.tryLock()) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("等待獲取鎖"); } try { for (int i = 0; i < 5; i++) { System.out.println(tName + ":" + i); } Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }); Thread t2 = new Thread(new Runnable() { public void run() { String tName = Thread.currentThread().getName(); while (!lock.tryLock()) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("等待獲取鎖"); } try { for (int i = 0; i < 5; i++) { System.out.println(tName + ":" + i); } } catch (Exception e) { System.out.println(tName + "出錯了!!!"); } finally { System.out.println(tName + "釋放鎖!!"); lock.unlock(); } } }); t1.start(); t2.start(); } }
3:lock.trylock(long time, TimeUnit unit)若是獲取了鎖定當即返回true,若是別的線程正持有鎖,會等待參數給定的時間,在等待的過程當中,若是獲取了鎖定,就返回true,若是等待超時,返回false;unit是time的時間單位好比TimeUnit.SECONDS就是表示秒code
4:lock.lockInterruptibly()若是獲取了鎖定當即返回,若是沒有獲取鎖定,當前線程處於休眠狀態,直到或者鎖定,或者當前線程被別的線程中斷。也就是說如何線程沒有被中斷和lock.lock()的做用同樣。可是如何線程被中斷了,那麼此時這個線程不會有任何的響應,想象這麼一個場景,線程A和線程B同時執行任務,可是必須等待線程B先執行,可是執行過程當中忽然線程A忽然被中斷,那麼這個時候就可能出現死鎖,哪怕是在finally中加入unlock,這個時候咱們就要採用lockInterruptibly()了。代碼以下htm
public class Lock3 { public static void main(String[] args) { final Lock lock = new ReentrantLock(); final Thread thread1 = new Thread(new Runnable() { public void run() { try { TimeUnit.SECONDS.sleep(2); System.out.println("等待被中斷"); lock.lockInterruptibly(); } catch (InterruptedException e) { System.out.println("我被中斷了"); } finally { lock.unlock(); } } }); Thread thread2 = new Thread(new Runnable() { public void run() { lock.lock(); try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { } thread1.interrupt(); System.out.println("線程1已經被中斷"); } }); thread1.start(); thread2.start(); } }
在開發中咱們最好的願望就是寫的時候加鎖,可是讀的時候不加鎖這樣會大大的提高效率,可是採用synchronized卻沒法知足咱們的要求,若是在讀的方法面前加鎖那麼全部的讀都須要等待,若是不加鎖的話那麼若是如今A,B2個線程讀取,C線程寫入可能致使的後果就是A,B2個線程取得數據不一致,明明同一種業務場景可是獲取值卻不一樣。好了lock的讀鎖和寫鎖幫助咱們實現這種功能。對象
public class ReadWriteLockTest { private int value = 0; ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public void add(int value) { Lock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { TimeUnit.SECONDS.sleep(3); System.out.println("添加開始時間:" + new Date()); this.value += value; } catch (InterruptedException e) { e.printStackTrace(); } finally { writeLock.unlock(); } } public void getValue() { Lock readLock = readWriteLock.readLock(); readLock.lock(); try { TimeUnit.SECONDS.sleep(1); System.out.println("獲取開始時間:" + new Date()); System.out.println(value); } catch (InterruptedException e) { } finally { readLock.unlock(); } } public static void main(String[] args) { final ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest(); Runnable task1 = new Runnable() { public void run() { readWriteLockTest.add(100); } }; Runnable task2 = new Runnable() { public void run() { readWriteLockTest.getValue(); } }; ExecutorService service = Executors.newCachedThreadPool(); for (int i=0;i<2;i++){ service.execute(task1); } for (int i=0;i<2;i++){ service.execute(task2); } for (int i=0;i<2;i++){ service.execute(task1); } service.shutdown(); } }
運行結果:
從這個結果咱們很明顯的能夠總結讀鎖和寫鎖
第一:若是執行寫的時候,讀和寫必須等待
第二:若是執行讀的時候,寫必須等待,而讀卻不用等待
也就是說讀和寫必須存在前後順序,不論是先讀仍是先寫。
3:總結
相同點:lock能實現synchronized全部能夠實現的
不一樣點:
1:lock不容易出現死鎖,而synchronized若是某個線程出現異常就會產生死鎖
2:lock更加靈活,能夠經過tryLock來驗證是否獲取鎖,在線程中斷也一樣能夠處理
3:lock有讀寫鎖在併發量大的時候具備很大的優點,由於讀的狀況通常會比寫多不少