一。讀寫鎖java
傳統的同步鎖就是獨佔式鎖,當線程使用資源時候保持獨佔,不管讀寫。當人們發現請求隊列(假設)中相鄰請求爲讀-讀的時候,阻塞是一種浪費資源的操做。好比公告板,全部路過的人(請求)都是讀操做,並無由於你和他在讀的時候對內容形成了改變,因此在模型中,讀與讀操做不須要阻塞。而讀寫相鄰則須要進行獨佔式操做了,由於寫未完成的時候,信息是不完整的,此時讀出來的信息有多是錯誤的,因此寫必然要保持獨佔式操做。而在應用程序中,讀的頻率是寫的好幾倍,也就是說若是讀-讀是不阻塞的,那麼對性能來講是毋庸置疑的提高。多線程
Java中存在一種鎖,名曰:ReentrantReadWriteLock。他能夠實現內存中對資源操做的讀寫鎖,讀與讀是不阻塞的。併發
import java.util.Random; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Created by MacBook on 2018/3/10. */ public class ReadWriteLockDemo { private static Lock relock = new ReentrantLock(); private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private static Lock readLock = readWriteLock.readLock(); private static Lock writeLock = readWriteLock.writeLock(); private int value; public Object handleRead(Lock lock) throws Exception{ try{ lock.lock(); Thread.sleep(1000); return value; }finally { lock.unlock(); } } public void handleWrite(Lock lock,int index) throws Exception{ try{ lock.lock(); Thread.sleep(1000); value = index; }finally { lock.unlock(); } } public static void main(String[] args){ ReadWriteLockDemo demo = new ReadWriteLockDemo(); Runnable readThread = new Runnable() { @Override public void run() { try{ System.out.println("read:"+demo.handleRead(readLock)); }catch (Exception e){ e.printStackTrace(); } } }; Runnable writeThread = new Runnable() { @Override public void run() { try{ // demo.handleWrite(relock,new Random().nextInt()); demo.handleWrite(writeLock,new Random().nextInt()); System.out.println("id:"+Thread.currentThread().getId()+" done!"); }catch (Exception e){ e.printStackTrace(); } } }; for(int i=0;i<18;i++){ new Thread(readThread).start(); } for(int i=0;i<18;i++){ new Thread(writeThread).start(); } } }
此demo使用了重入鎖和讀寫鎖的對比,在主程序中分別新建18個讀寫操做,若是使用了讀操做,則打印的讀操做是連續的;若是使用了重入鎖,則可能的狀況是讀寫相鄰打印,而且都是阻塞的,讀者能夠自行測試體會。dom
二。對象監視器Conditionide
在JDK實現了Lock來簡化synchronized以後,Condition做爲簡化監視器而存在。Condition的await方法和signal方法對應對象的wait和signal。高併發
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * Created by MacBook on 2018/3/10. */ public class ConditionAndLock implements Runnable{ static ReentrantLock lock = new ReentrantLock(); static Condition condition = lock.newCondition(); public void run(){ try{ lock.lock(); condition.await(); System.out.println("thread is running"); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } public static void main(String[] args){ ConditionAndLock c = new ConditionAndLock(); Thread t = new Thread(c); t.start(); lock.lock(); System.out.println("signal all"); condition.signalAll(); lock.unlock(); } }
三。倒計時器CountDownLatch性能
多線程中,須要知道這批線程的最大完成任務時間,也就是從第一個任務開始到最後返回這段時間的時長,那麼倒計時器是必不可少的。就像各項資源準備完畢才進行下一步操做的模型同樣,CountDownLatch就是這樣的多線程模型。等到全部任務調用了計數器,而且計數器總數到達某個數量時候,它纔會將阻塞代碼放開,讓主線程往下走。測試
import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 倒計時器 * Created by MacBook on 2018/3/10. */ public class CountDownLatchDemo implements Runnable{ static CountDownLatch end = new CountDownLatch(10); static CountDownLatchDemo demo = new CountDownLatchDemo(); public void run(){ try{ Thread.sleep(new Random().nextInt(10)*1000); System.out.println(Thread.currentThread().getId()+" check complete!"); end.countDown(); }catch (Exception e){ e.printStackTrace(); } } public static void main(String[] args) throws Exception{ ExecutorService service = Executors.newFixedThreadPool(10); for(int i=0;i<10;i++){ service.submit(demo); } end.await(); System.out.println("fire"); service.shutdown(); } }
await方法是阻塞倒計時器所在線程的方法,等到線程池service中調用countDown方法到達必定的數量(此處是10)以後,主線程的await方法纔會過去。ui
四。信號量this
信號量這個東西就比較玄乎了,有點像准入許可,拿到信號准入的時候才往下執行。就像是有一批人拿號,只有號碼區間在某個範圍的人能進去辦事,而後辦完事就會讓資源釋放,號碼區間日後移。然而在信號量中應該算是複用類型的,歸還了key值,將key值返回給下一個申請者。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; /** * Created by MacBook on 2018/3/10. */ public class SemapDemo implements Runnable{ final Semaphore semp = new Semaphore(5); public void run(){ try{ semp.acquire(); Thread.sleep(2000); System.out.println(Thread.currentThread().getId()+" done!"); semp.release(); }catch (Exception e){ e.printStackTrace(); } } public static void main(String[] args){ ExecutorService executorService = Executors.newFixedThreadPool(5); SemapDemo semapDemo = new SemapDemo(); for(int i=0;i<20;i++){ executorService.submit(semapDemo); } executorService.shutdown(); } }
在acquire得到key以後,操做讀寫,以後release。
五。柵欄
柵欄和倒計時器很像,就是攔住一堆線程,等到線程數達到某個設定值以後同時把它們放出去。可是不一樣的是,它能夠每次設定值達成時候運行定製線程中的run方法。就像是每次一個欄,夠數就放。
import java.util.Random; import java.util.concurrent.CyclicBarrier; /** * Created by MacBook on 2018/3/10. */ public class CylicBarrierDemo { public static class Soldier implements Runnable{ private String soldier; private final CyclicBarrier cyclicBarrier; Soldier(String soldier,CyclicBarrier cyclicBarrier){ this.soldier = soldier; this.cyclicBarrier = cyclicBarrier; } public void run(){ try{ cyclicBarrier.await(); doWork(); cyclicBarrier.await(); }catch (Exception e){ e.printStackTrace(); } } public void doWork(){ try{ Thread.sleep(Math.abs(new Random().nextInt()%10000)); }catch (Exception e){ e.printStackTrace(); } System.out.println(soldier + " done!"); } } public static class BarrierRun implements Runnable{ boolean flag; int n; public BarrierRun(boolean flag,int n){ this.flag = flag; this.n = n; } public void run(){ if(flag){ System.out.println("士兵:"+n+"個 done!"); }else { System.out.println("士兵:"+n+"個 集合完畢!"); flag = true; } } } public static void main(String[] args){ final int n = 10; Thread[] allSoldier = new Thread[n]; boolean flag = false; CyclicBarrier cyclic = new CyclicBarrier(n,new BarrierRun(flag,n)); System.out.println("集合"); for(int i =0; i < n ; i++){ System.out.println("士兵 "+i+" 報道"); allSoldier[i] = new Thread(new Soldier("士兵"+i,cyclic)); allSoldier[i].start(); } } }
例中CyclicBarrier有兩個參數,前一個就是提到的設定值,後一個就是定製線程了。每當到達設定值的時候會觸發定製線程。
每一個階段完成都會調用一下定製線程。
六。LockSupport提供線程掛起操做的支持類
正如Condition使得原有的Object監視器封裝成了新類,LockSupport提供使線程park和unpark之類的操做。
import java.util.concurrent.locks.LockSupport; /** * Created by MacBook on 2018/3/10. */ public class LockSupportDemo { public static Object u = new Object(); static ChangeObjectThread t1 = new ChangeObjectThread("t1"); static ChangeObjectThread t2 = new ChangeObjectThread("t2"); public static class ChangeObjectThread extends Thread{ public ChangeObjectThread(String name){ super.setName(name); } public void run(){ synchronized (u){ System.out.println("in "+getName()); LockSupport.park(); } } } public static void main(String[] args) throws Exception{ t1.start(); Thread.sleep(100); t2.start(); LockSupport.unpark(t1); LockSupport.unpark(t2); t1.join(); t2.join(); } }
它在park時候線程會變成wait狀態,而不是runnable。
來自《Java高併發程序設計》的讀書筆記