並發與並行,進程與線程不僅是操作系統中及其重要的概念,也是並發編程入門
必須要理解的核心知識。
順序編程:程序中的所有事物在任意時刻都只能執行一個步驟
並發:在同一時間段內,需要處理多個任務,而在每個時間點又只能處理一個,這就是並發。
假設我們要把多個任務分配給處理機,如果這台機器有多個處理器,顯然可以同時執行這些任務,這就是並行。
不同於並行,並發的目的旨在最大限度的提高程序在單處理器上的效率。前者是在物理上的同時發生,而並發是在邏輯上的同時發生。如圖,如果要在同一個時間段內處理task1, task2,需要在任務之間來回切換,完成並發執行
圖:一個簡單並發的例子
2. Java的多線程和並發性
Java是一種多線程語言,並發是Java非常重要的高級特性之一。對於分布式系統甚至於web系統的開發者來說,學會高效的並發編程至關重要。例如,web系統是Java的常見應用之一,最基本的java web庫類 Servlet就具備天生的多線程特性,圖形用戶界面類似於Swing和SWT也具備線程安全的機制。如果你不了解並發,你很難很好的使用好這些工具。
3. 進程和線程
進程可以定義為一個執行中的程序的實例,進程擁有自己的地址空間,以及用來記錄進程的進程控制塊(PCB)。想到實現並發,人們最先想到的便是通過進程。在多任務操作系統中,CPU會周期性地從一個進程切換到另一個進程。這種實現方式非常理想化,由於不同進程處於不同的地址空間,彼此之間不會干涉,這使得實現起來更為容易。
可惜的是,雖然具有以上優點,但由於進程通常都會有開銷和數量的限制,想要使用進程來並發編程顯然不夠經濟。
圖:進程的開銷( 任務管理器)
函數型語言可以將並發任務彼此隔離,這種專門的並發語言為處理大量並發任務提供了方便。
線程是在單一進程中創建的任務,線程與線程之間共享進程的地址空間和內存資源,Java的並發就是在順序語言的基礎上引入線程機制,通過多線程 之間的協作來實現並發。
多線程並發的好處是可以共享資源,開銷較小,然而同時也使得系統的復雜性增加。不過與程序設計的方便性,資源的負載均衡實現相比,復雜性代價也變得微不足道了。
4. 線程的創建
Java中創建線程,經常用到java.lang.Thread類,如下創建一個線程:
Thread thread = new Thread();
調用start()方法啟動線程
thread.start();
由於我們沒有繼承Thread類,重載Thread的run()方法,所以在start()之後線程沒有任何動作。
要想讓線程執行我們想要它完成的任務,還需要創建Thread的子類來重載run()方法。
5. 創建Thread子類
舉個栗子
public class MyThread extends Thread { public void run(){ System.out.println("ACFLOOD!"); } }
通過如下代碼來運行MyThread
MyThread myThread = new MyThread(); myThread.start();
6. 實現Runnable接口
我們還可以通過實現Runnable接口來實現run()方法,你可能會問,Runnable和Thread的區別是什麼? 其實,在Java的多線程機制中,Runnable的實現就相當於一個Task,也就是我們想讓線程完成的任務。Thread的目的是用來驅動這些任務,我們想要執行Task,只需要把Runnable的實例放入Thread中。比如下面的倒計時任務LiftOff
public class LiftOff implements Runnable { protected int countDown = 10; //Default private static int taskCount = 0; private final int id = taskCount++; public LiftOff(){} public LiftOff(int countDown){ this.countDown = countDown; } public String status() { return "#" + id + "(" + (countDown > 0 ? countDown : "LiftOff!")+")."; } public void run(){ while(countDown-- > 0){ System.out.print(status()); Thread.yield(); //對線程調度的建議 } } }
我們可以直接調用run()方法來執行。此時程序依舊是順序執行,即只有主線程在運行,並沒有新的線程被創建。
public class MainThread { public static void main(String[] args){ LiftOff launch = new LiftOff(); laucn.run(); } }
更好的辦法是交給Thread類來調度
public class BasicThreads{ Thread t = new Thread(new LiftOff()); t.start(); System.out.println("Waiting for LiftOff"); }
在這個例子中,start()方法執行之後返回,之後輸出main()主線程中的waiting for liftoff,run()在另一個線程最後執行完成。
7. 總結
通過本文的學習,相信讀者對並發的基本概念,並發基本的實現原理,以及java多線程對於線程的創建有了初步的了解。在本系列的後續部分將會繼續探討並發編程中的細節。
如果覺得本文對您有所幫助的話,就給俺點個贊吧~