JAVA 多線程(8):synchronized 的同夥lock
Lock:
lock對象功能類似synchronized ,但是更加方便,或者說有更多的功能。
實現類:
1.ReentrantLock
2.ReentrantReadWriteLock : 讀寫互斥,比1功能再多一點
一、ReentrantLock
首先回顧一下synchronized:
public void test(){ synchronized (this){ System.out.println(Thread.currentThread().getName()+"begin"); try{ Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"end"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args){ TestRLock testRLock= new TestRLock(); Runnable runnable = new Runnable() { @Override public void run() { testRLock.test(); } }; Thread t = new Thread(runnable); Thread t2 = new Thread(runnable); t.start(); t2.start(); }
輸出:
結果是同步的。
看一下lock 實現:
private Lock lock = new ReentrantLock(); public void test() { lock.lock(); System.out.println(Thread.currentThread().getName() + "begin"); try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "end"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }
輸出結果與上面的一致。
相比synchronized個人感覺更清晰,至少不用去找大括號了。
關於lock 的wait與notify:
lock 同樣有等待/通知機制,只不過調用的方法名稱不同,並且細粒度更強。
在調用時,需要聲明一個條件對象 condition(跟wait一樣,必須在lock範圍內),如下;
private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void test() { lock.lock(); System.out.println(Thread.currentThread().getName() + "begin"); try { condition.await(); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "end"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void test2() { lock.lock(); System.out.println(Thread.currentThread().getName() + "begin"); try { condition.signal(); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "end"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main(String[] args) { TestRLock testRLock = new TestRLock(); Runnable runnable = new Runnable() { @Override public void run() { testRLock.test(); } }; Runnable runnable2 = new Runnable() { @Override public void run() { testRLock.test2(); } }; Thread t = new Thread(runnable); Thread t2 = new Thread(runnable2); t.start(); try { Thread.sleep(2000); t2.start(); } catch (InterruptedException e) { e.printStackTrace(); } }
輸出:
wait 對應的是 await,notify 對應的是signal,notifyAll 對應的是signalAll。
區別:使用condition 的好處是可以控制通知部分線程,因為conditon 可以創建多個的緣故。
如下:
private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); private Condition condition2 = lock.newCondition(); public void test() { lock.lock(); System.out.println(Thread.currentThread().getName() + "begin"); try { condition.await(); System.out.println(Thread.currentThread().getName() + "end"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void test3() { lock.lock(); System.out.println(Thread.currentThread().getName() + "begin"); try { condition2.await(); System.out.println(Thread.currentThread().getName() + "end"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void test2() { lock.lock(); condition.signal(); System.out.println(Thread.currentThread().getName() + "喚醒了等待1"); lock.unlock(); } public static void main(String[] args) { TestRLock testRLock = new TestRLock(); Runnable runnable = new Runnable() { @Override public void run() { testRLock.test(); } }; Runnable runnable2 = new Runnable() { @Override public void run() { testRLock.test2(); } }; Runnable runnable3 = new Runnable() { @Override public void run() { testRLock.test3(); } }; Thread t = new Thread(runnable, "我是等待1"); Thread t3 = new Thread(runnable3, "我是等待2"); Thread t2 = new Thread(runnable2, "我來喚醒1"); t.start(); t3.start(); try { Thread.sleep(3000); t2.start(); } catch (InterruptedException e) { e.printStackTrace(); } }
輸出:
例子中增加了一個conditon 實例,並且conditon2的等待是沒有人通知他的。由此實現的是通知部分等待線程。
其他功能:
1.getHoldCount() :獲取當前鎖定的個數,在unlock之前保持此鎖的個數。
2.getQueueLength():獲取等待鎖釋放的線程數
3.getWaitQueyeLenth():獲取調用await的conditon的線程數,如果有5個線程同時調用了同一個codition的await,且都沒有調用signal 釋放鎖,那麽此值為5.
4.hasQueueThread():查詢某個線程是否獲得了鎖 ,返回值為布爾類型
5.hasQueueThreads():查詢是否有線程在等待調用鎖,返回值為布爾
6.hasWaiters():查詢是否有線程在等待conditon 通知
7.isFair():判斷是否為公平鎖。 公平鎖-》是否為先進先出。在創建鎖實例時是否設定。
8.isHeldByCurrentThread():當前線程是否保持了該鎖。
9.isLocked():查詢lock是否被任意線程保持。
還有其他的一些用法,在此不一個個敲字了。。。
二、ReentrantReadWriteLock
從名字看出,它持有2把鎖,一把read鎖不互斥,也就是共享鎖,另一把write鎖互斥。
讀讀共享:
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public void test() { lock.readLock().lock(); System.out.println(Thread.currentThread().getName() + System.currentTimeMillis()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } lock.readLock().unlock(); } public void test2() { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName() + System.currentTimeMillis()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } lock.writeLock().unlock(); } public static void main(String[] args) { TestRLock testRLock = new TestRLock(); Runnable runnable = new Runnable() { @Override public void run() { testRLock.test(); } }; Thread t = new Thread(runnable, "A"); Thread t2 = new Thread(runnable, "B"); t.start(); t2.start(); }
輸出:
寫寫互斥:
把main主線程調用中runnable run 方法改為調用test2
輸出:
由結果看出,寫鎖必須等待,也就是同步操作。
讀寫互斥(凡是有寫入操作的,都會等待)
main 方法修改為:
public static void main(String[] args) { TestRLock testRLock = new TestRLock(); Runnable runnable = new Runnable() { @Override public void run() { testRLock.test(); } }; Runnable runnable2 = new Runnable() { @Override public void run() { testRLock.test2(); } }; Thread t = new Thread(runnable, "A"); Thread t2 = new Thread(runnable2, "B"); t.start(); t2.start(); }
輸出:
總結:
其實如果沒有太需要的話使用synchronized 就足夠了,但是如果追求控制欲的同學可以使用lock,對麽?
JAVA 多線程(8):synchronized 的同夥lock