一開始咱們作的都是「順序編程」,可是有時候程序純順序執行的性能並不高,而且對於部分問題順序執行程序並不能很好地解決。程序員
這時候「併發」就是一個很好的解決方案了,「併發」的含義其實很簡單,即並行地執行程序中的多個部分。這些部分要麼看起來在併發地執行(單處理器環境下經過競爭 cpu 時間片實現同時執行效果),要麼在多處理器環境下真正同時執行。編程
併發「具備可論證的肯定性,可是實際上具備不可肯定性」。這是研究併發問題的最強理由:若是視而不見,你就會遭其反噬。-- 《Java 編程思想》多線程
雖然書裏是這麼講,可是在實際開發當中,碰到「不可肯定性」的機率比較低(可能我經驗不夠?),一般碰到的問題都是因爲考慮問題不全面,代碼 bug 致使,不多碰到所謂的「不可肯定性」。固然,雖然沒碰到過,不過聽說碰到了多是很是坑爹的,一般連復現問題都比較困難。併發
有時候,雖然你沒有主動開啓線程使用併發,可是你仍是沒法避免併發,Java Web 開發中基本的 Web 類庫、Servlet 具備天生的多線程性。負載均衡
併發一般是用來提升運行在「單處理器」上的程序的性能,可是一般在單處理器上運行的併發程序比該程序的全部部分都順序執行的開銷大,由於增長了所謂的「上下文切換」的代價。那既然開銷變大,又怎麼會提升性能呢?答案是:「阻塞」,最典型的就是 I/O。從性能的角度看,若是沒有任務會阻塞,那麼在單處理器機器上使用併發就沒有任何意義。性能
在單處理器系統中的性能提升的常見示例是「事件驅動的編程」。若是不使用併發,則產生可響應用戶界面的惟一方式就是全部的任務都週期性地檢查用戶的輸入。經過建立單獨的執行線程來響應用戶的輸入,即便這個線程在大多數時間裏都是阻塞的,可是程序能夠保證具備必定程度的可響應性。—— 《Java 編程思想》操作系統
實現併發最直接的方式是在操做系統級別使用進程。操做系統一般會將進程互相隔離開,進程使用的資源也都是隔離的,進程間相互影響較小,編程也相對簡單。與此相反的是,像 Java 所使用的這種併發系統會共享諸如內存和 I/O 這樣的資源,所以編寫多線程程序最基本的困難在於在協調不一樣線程驅動的任務之間對這些資源的使用,以使得這些資源不會同時被多個任務訪問。線程
多線程系統對可用的線程數量的限制一般都會是一個相對較小的數字,有時就是數十或者數百這樣的數量級。這個數字在程序控制範圍以外可能會發生變化——它可能依賴於平臺,或者在 Java 中,依賴於 Java 的版本。設計
在 Java 中,一般要假定你不會得到足夠多的線程,從而使得能夠爲程序中的每一個任務都提供一個線程。解決這個問題的典型方式是使用「協做多線程」。進程
Java 的線程機制是「搶佔式」的,這表示調度機制會週期性地中斷線程,將上下文切換到另外一個線程,從而爲每一個線程都提供時間片。
在協做式系統中,每一個任務都會自動地放棄控制,這要求程序員要有意識地在每一個任務中插入某種類型的讓步語句。
協做式系統的優點是雙重的:上下文切換的開銷一般比搶佔式系統要低廉許多,而且對能夠同時執行的線程數量在理論上沒有任何限制。
併發須要付出代價,包含複雜性代價,可是這些代價與在程序設計、資源負載均衡以及用戶方便使用方面的改進相比,就顯得微不足道了。線程使你可以建立更加鬆散耦合的設計,不然,你的代碼中各個部分都必須顯式地關注那些一般能夠由線程來處理的任務。