第一章 並發編程的挑戰
阿新 • • 發佈:2018-09-20
必須 currency 更新 err 如果 ide 數據庫操作 time lee
挑戰一:上下文切換
多線程一定比單線程快麽?
public class ConcurrencyTest { private static final long count = 10001; public static void main(String[] args) throws InterruptedException { concurrency(); serial(); } private static void concurrency() throws InterruptedException{ Long start= System.currentTimeMillis(); Thread thread = new Thread(new Runnable() { @Override public void run() { int a = 0; for (long i = 0; i < count; i++) { a += 5; } } }); thread.start();int b = 0; for (long i = 0; i < count; i++) { b--; } thread.join(); long time = System.currentTimeMillis() - start; System.out.println("concurrency:" + time + "ms, b = " + b); } private static void serial(){ long start = System.currentTimeMillis();int a = 0; for (long i = 0; i < count; i++) { a += 5; } int b = 0; for (long i = 0; i < count; i++) { b--; } long time = System.currentTimeMillis() - start; System.out.println("serial:" + time + "ms, b = " + b + ", a=" + a); } }
輸出
答案是並不一定,當測試量達到一百萬的時候,並發才能比串行優勢點(本代碼環境結果);
線程創建和上下文切換都是需要開銷的。
如何減少上下文的切換?
- 無鎖並發編程:可以使用一些方法避免使用鎖,如將數據的ID按照Hash算法取模分段,不同的線程處理不同段的數據。
- CAS算法:Java的Atomic包使用CAS算法更新數據,而不需要加鎖。
- 使用最少線程:避免創建不需要的線程。
- 協程:在單線程裏實現多任務的調度,並在單線程裏維持多個任務間的切換。
挑戰二:死鎖
public class DeadLockDemo { private static String A = "A"; private static String B = "B"; public static void main(String[] args) { new DeadLockDemo().deadLock(); } private void deadLock(){ Thread thread1 = new Thread(new Runnable() { @Override public void run() { synchronized (A){ try { Thread.sleep(2000); } catch (InterruptedException e){ e.printStackTrace(); } synchronized (B){ System.out.println("1"); } } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { synchronized (B){ synchronized (A){ System.out.println("2"); } } } }); thread1.start(); thread2.start(); } }
避免死鎖的幾個常見方法:
- 避免一個線程同時獲取多個鎖
- 避免一個線程在鎖內同時占用多個資源,盡量保證每個鎖只占一個資源
- 嘗試使用定時鎖,使用lock.tryLock(timeout)來替代使用內部鎖機制
- 對於數據庫鎖,加鎖和解鎖必須在一個數據庫連接裏
挑戰三:資源限制的挑戰
(1) 什麽是資源限制?
指在進行並發編程時,程序的執行速度受限於計算機硬件資源或軟件資源。
(2) 資源限制引發的問題?
在並發編程中,將代碼執行速度加快的原則是將代碼中串行的部分變成並發執行,但是如果將某段串行的代碼並發執行,因為受限於資源,仍然在串行執行,這時候程序不僅不會加快執行,反而會更慢,因為增加了上下文切換和資源調度的時間。
(3)如何解決資源限制的問題?
對於硬件限制,可以考慮集群並行執行程序。既然單機的資源有限制,就讓程序在多機上運行。比如使用ODPS、Hadoop或者自己搭建服務器集群;
對於軟件限制,可以考慮使用資源池將資源復用。比如使用連接池將數據庫和Socket連接復用,或者在調用對方webService接口獲取數據時,只建立一個連接。
(4)如何在資源限制的情況下進行並發編程?
根據不同的資源限制調整程序的並發度,比如下載文件程序依賴於兩個資源——寬帶和硬盤讀寫速度。有數據庫操作時,設計數據庫連接數,如果SQL語句執行非常快,而線程的數量比數據庫連接數大很多,則某些線程會被阻塞,等待數據庫連接。
第一章 並發編程的挑戰