標籤:queue zed catch 迴圈 tac cas 鎖對象 好的 ant
為什麼需要並行? – 業務要求 – 效能 並行計算還出於業務模型的需要 – 並不是為了提高系統效能,而是確實在業務上需要多個執行單元。 – 比如HTTP伺服器,為每一個Socket串連建立一個處理線程 – 讓不同線程承擔不同的業務工作 – 簡化任務調度 Linus Torvalds :並行計算只有在 *影像處理* 和 *服務端編程* 2個領域可以使用,並且它在這2個領域確實有著大量廣泛的使用。但是在其它任何地方,並行計算毫無建樹! 計算密集型 在多核時代,一般沒有必要特別區分並發和並行 同步(synchronous)和非同步(asynchronous) ? 並發(Concurrency)和並行(Parallelism) ? 臨界區 ? 阻塞(Blocking)和非阻塞(Non-Blocking) – 阻塞和非阻塞通常用來形容多線程間的相互影響。比如一個線程佔用了臨界區資源,那麼其它所有需要這個資源的線程就必須在這個臨界區中進行等待,等待會導致線程掛起。這種情況就是阻塞。此時,如果佔用資源的線程一直不願意釋放資源,那麼其它所有阻塞在這個臨界區上的線程都不能工作。 – 非阻塞允許多個線程同時進入臨界區 ? 鎖(Deadlock)、饑餓(Starvation)和活鎖(Livelock) ? 並行的層級並發層級 – 阻塞 – 非阻塞 – 無障礙 – 無鎖 – 無等待有關並行的2個重要定律 ? Amdahl定律(阿姆達爾定律) ? Gustafson定律(古斯塔夫森)進程切換是一個重量級的操作,需要消耗大量的計算資源在Java中的線程會直接映射到作業系統的線程上去Thread t1 = new Thread();t1.start(); // 開啟線程進入就緒狀態Thread t2 = new Thread();t2.run(); // 不能開啟線程,在本線程內執行run方法 繼承Thread重寫run方法,MyThread extends Threadnew Thread(new Runnable() {}).start();Thread.stop()不建議使用@Deprecated,太過暴力,類似於Linux強制殺死進程:kill -9 thread_idpublic static native void sleep(long millis) throws InterruptedExceptionpublic void run() { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println("Interruted!"); break; } try { Thread.sleep(2000); } catch (InterruptedException e) { System.out.println("Interruted When Sleep"); // 設定中斷狀態,拋出異常後會清除中斷標記位 ******** Thread.currentThread().interrupt(); } Thread.yield(); }}suspend()、resume()等待線程結束(join)和謙讓(yeild) join的本質: while(isAlive()) { wait(0); } 線程執行完畢後,系統會調用notifyAll(); ===> 可以不需要線上程執行個體上使用wait()和notifyAll()守護線程 ? 在後台默默地完成一些系統性的服務,比如記憶體回收線程、 JIT線程就可以理解為守護線程 ? 當一個Java應用內,只有守護線程時,Java虛擬機器就會自然退出 Thread t = new DaemonT(); t.setDaemon(true); t.start(); 高優先順序的線程更容易再競爭中獲勝基本的線程同步操作 ? synchronized – 指定加鎖對象:對給定對象加鎖,進入同步代碼前要獲得給定對象的鎖。 public void run() { for(int j = 0; j < 10000000; j++) { synchronized(instance) { i++; } } } – 直接作用於執行個體方法:相當於對當前執行個體加鎖,進入同步代碼前要獲得當前執行個體的鎖。 public synchronized void increase(){ i++; } – 直接作用於靜態方法:相當於對當前類加鎖,進入同步代碼前要獲得當前類的鎖。 public static synchronized void increase(){ i++ } ? Object.wait() Obejct.notify() public static class T1 extends Thread { public void run() { synchronized (object) { System.out.println(System.currentTimeMillis()+":T1 start!"); try { System.out.println(System.currentTimeMillis() + ":T1 wait for object "); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(System.currentTimeMillis()+":T1 end!"); } } } public static class T2 extends Thread { public void run() { synchronized (object) { System.out.println(System.currentTimeMillis() +":T2 start! notify one thread"); object.notify(); System.out.println(System.currentTimeMillis()+":T2 end!"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } } 1425224592258:T1 start! 1425224592258:T1 wait for object 1425224592258:T2 start! notify one thread 1425224592258:T2 end! 1425224594258:T1 endnotify() VS notifyAll() notify()隨機喚醒一個線程,notifyAll()喚醒全部等待此監視器的線程,讓他們去競爭這個監視器的使用權》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》Java記憶體模型和安全執行緒 ? 原子性 原子性是指一個操作是不可中斷的。即使是在多個線程一起執行的時候,一個操作一旦開始,就不會被其它線程幹擾。 ? 有序性 在並發時,程式的執行可能就會出現亂序。 一條指令的執行是可以分為很多步驟的(*資料旁路技術*&*指令重排*可以使流水線更加流暢,指令重排不能出現語義問題) – 取指 IF – 解碼和取寄存器運算元 ID – 執行或者有效地址計算 EX – 儲存空間訪問 MEM – 寫回 WB ? 可見度 可見度是指當一個線程修改了某一個共用變數的值,其他線程是否能夠立即知道這個修改。 – 編譯器最佳化 – 硬體最佳化(如寫吸收,批操作) Java虛擬機器層面的可見度(volatile) ? Happen-Before ? 程式順序原則:一個線程內保證語義的串列性 ? volatile規則:volatile變數的寫,先發生於讀,這保證了volatile變數的可見度 ? 鎖規則:解鎖(unlock)必然發生在隨後的加鎖(lock)前 ? 傳遞性:A先於B,B先於C,那麼A必然先於C ? 線程的start()方法先於它的每一個動作 ? 線程的所有操作先於線程的終結(Thread.join()) ? 線程的中斷(interrupt())先於被中斷線程的代碼 ? 對象的建構函式執行結束先於finalize()方法 ? 安全執行緒的概念 指某個函數、函數庫在多線程環境中被調用時,能夠正確地處理各個線程的局部變數,使程式功能正確完成。 ReentrantLock(重入鎖 )是synchronized關鍵字的增強,目前兩者效能不相上下。ReentrantLock 可重新進入 可多次加鎖 可中斷 發生死結時,可啟用一個守護線程去中斷某一線程從而達到解鎖目的 可限時 避免死結和長期等待鎖 公平鎖 公平鎖雖然不會產生饑餓但是由於公平鎖需要解決排隊問題(先到先得),所以效能較非公平鎖差,沒有特殊要求沒必要使用公平鎖。JDK並發包--並發容器及典型源碼分析 ?? 集合封裝 ? HashMap --> 適用小並發量,串列解決方案,非高並發解決方案 public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) { return new SynchronizedMap<>(m); } ? List public static <T> List<T> synchronizedList(List<T> list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : new SynchronizedList<>(list)); } ? Set public static <T> Set<T> synchronizedSet(Set<T> s) { return new SynchronizedSet<>(s); } ?? ConcurrentHashMap ConcurrentHashMap(HashMap底層使用數組實現,因此為實現大規模高並發,可將整個數組分成N個段<Segment>,一個就可供N個線程同時寫入資料,理論提高效率N倍) put()方法各個Segment有各自的鎖,get()方法無鎖,但是在size()方法中需要拿到所有Segment的鎖後才能統計資料,但size()方法並非一個高頻率調用的函數。 之所是高效能,是因為不會隨隨便便就加鎖,而是經過自旋等待在有必要的時候才加鎖。 ?? BlockingQueue 阻塞隊列,是一個介面,安全執行緒,不是一個高效能的容器,但是BlockingQueue是一個非常好的**在多個線程中共用資料的容器** 若為空白隊列,此時有線程嘗試讀取資料,則此讀的線程會等待,直到有另外線程往隊列中寫入資料,則讀的線程就會被喚醒並且讀取資料; 若隊列已經寫滿了,則寫入的線程就會等待,直到有線程讀取資料有空閑空間後才能寫入隊列; 因此Blocking會引起線程阻塞。 BlockingQueue作為生產者消費者容器很方便。 put()、take() 實現:ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue。 ?? ConcurrentLinkedQueue 類似ConcurrentHashMap的高效能Queue,內部使用了大量的無鎖操作。offer()、poll() ? BlockingQueue VS ConcurrentLinkedQueue 在並發編程中我們有時候需要使用安全執行緒的隊列。如果我們要實現一個安全執行緒的隊列有兩種實現方式:一種是使用**阻塞演算法**,另一種是使用**非阻塞演算法**。使用阻塞演算法的隊列可以用一個鎖(入隊和出隊用同一把鎖)或兩個鎖(入隊和出隊用不同的鎖)等方式來實現,而非阻塞的實現方式則可以使用迴圈CAS的方式來實現。
Java並發筆記