Java中給多線程編程提供了內置的支持,多線程是多任務的一種特別形式,它使用了更小的資源開銷。這裏須要知道兩個術語及其關係:進程和線程。html
進程:進程是系統進行資源分配和調度的一個獨立單位。一個進程包括由操做系統分配的內存空間,包含一個或多個線程。java
線程:線程是進程的一個實體,是CPU調度和分派的基本單位。它可與同屬一個進程的其餘的線程共享進程所擁有的所有資源。數據庫
(一)線程的生命週期編程
線程是一個動態執行的過程,從產生到死亡,這個過程稱爲線程的生命週期。線程的狀態有:新建狀態、就緒狀態、運行狀態、阻塞狀態、死亡狀態,以下圖所示,注意整個執行過程的實現:安全
(二):線程的建立多線程
Java提供了三種建立線程的方法:併發
在開發中,前兩種是經常使用的線程建立方式,下面來簡單說下:ide
(1)經過實現Runnable接口建立線程函數
public class RunnableDemo implements Runnable{ private Thread t; private String threadName; //構造方法 public RunnableDemo(String name) { this.threadName = name; System.out.println("建立線程:"+threadName); } //重寫run()方法 @Override public void run() { System.out.println("運行線程:"+threadName); try { for(int i=4;i>0;i--){ System.out.println("Thread: "+threadName+","+i); Thread.sleep(50); } }catch (Exception e) { System.out.println("Thread "+threadName+" 阻塞"); } System.out.println("Thread "+threadName+" 終止"); } //調用方法(爲了輸出信息,能夠忽略) public void start(){ System.out.println("啓動線程:"+threadName); if(t == null){ t = new Thread(this,threadName); t.start(); } } }
測試類:工具
public class RunnableTest { public static void main(String[] args) { RunnableDemo r1 = new RunnableDemo("T1"); r1.start(); RunnableDemo r2 = new RunnableDemo("T2"); r2.start(); } }
輸出爲:
建立線程:T1 啓動線程:T1 建立線程:T2 啓動線程:T2 運行線程:T1 Thread: T1 運行線程:T2 Thread: T2,4 Thread: T2,3 Thread: T1,3 Thread: T2,2 Thread: T1,2 Thread: T2,1 Thread: T1,1 Thread T2 終止 Thread T1 終止
從上面的實例能夠看出,經過實現Runnable接口建立線程的幾個要點:
(2)經過繼承Thread類建立線程
它本質上也是實現了 Runnable 接口的一個實例,因此這裏就不貼出代碼了,能夠按照上面的實例,更改class爲繼承便可,以下:
public class ThreadDemo extends Thread{}
Thread類的經常使用且重要的方法有:
注意:Java虛擬機容許應用程序併發的運行多個執行線程,利用多線程編程能夠編寫高效的程序,但線程太多,CPU 花費在上下文的切換的時間將多於執行程序的時間,執行效率反而下降,因此,線程並非建立的越多越好好,通常來講小到1個,大到10左右基本就夠用了。
固然,關於線程的其餘知識,如優先級、休眠、終止等,這裏就不作介紹了。
(三)synchronized關鍵字
Java提供了不少方式和工具來幫助簡化多線程的開發,如同步方法,即有synchronized關鍵字修飾的方法,這和Java的內置鎖有關。每一個Java對象都有一個內置鎖,若方法用synchronized關鍵字聲明,則內置鎖會保護整個方法,即在調用該方法前,須要得到內置鎖,不然就處於阻塞狀態。一個簡單的同步方法聲明以下:
public synchronized void save(){}
synchronized關鍵字也能夠修飾靜態方法,此時若調用該靜態方法,則會鎖住整個類。下面經過實例來講明下具體的使用:
同步線程類:
public class SyncThread implements Runnable { //定義計數變量並在構造函數中初始化 private static int count; public SyncThread(){ count = 0; } @Override public synchronized void run() { for(int i=0;i<5;i++){ //打印當前count值並進行累加操做,可分開寫 System.out.println(Thread.currentThread().getName() +":"+ (count++)); try { Thread.sleep(100); }catch (InterruptedException e) { e.printStackTrace(); } } } public int getCount(){ return count; } }
測試類:
public class SyncTest { public static void main(String[] args) { SyncThread sThread = new SyncThread(); //建立線程對象的同時初始化該線程的名稱 Thread t1 = new Thread(sThread,"SThread1"); Thread t2 = new Thread(sThread,"SThread2"); t1.start(); t2.start(); } }
輸出爲:
SThread1:0 SThread1:1 SThread1:2 SThread1:3 SThread1:4 SThread2:5 SThread2:6 SThread2:7 SThread2:8 SThread2:9
從上面能夠看出:一個線程訪問一個對象中的synchronized同步方法時,其餘試圖訪問該對象的線程將被阻塞。固然,你們能夠去掉synchronized關鍵字,看看會有什麼不一樣。這裏必需要注意:是訪問同一個對象的不一樣方法,如上面的對象sThread,如果不一樣的對象,則不受阻塞。這裏不作介紹了,你們能夠參考:Java中synchronized的用法,好好理解下。
(四)volatile關鍵字
相比較synchronized而言,volatile關鍵字是Java提供的一種輕量級的同步機制,爲域變量的訪問提供了一種免鎖機制,使用volatile修飾域至關於告訴虛擬機該域可能會被其餘線程更新,所以每次使用該域就要從新計算,而不是使用寄存器中的值。
若是讀操做的次數要遠遠超過寫操做,與鎖相比,volatile 變量一般可以減小同步的性能開銷。簡單的定義以下:
private volatile int count = 0;
volatile不具有原子特性,也不能用來修飾final類型的變量。要使volatile修飾的變量提供理想的線程安全,必須知足兩個條件:
這裏不作詳述了,但須要注意一點:避免volatile修飾的變量用於複合操做,如 num++,這個複合操做包括三步(讀取->加1->賦值),因此,在多線程的環境下,有可能線程會對過時的num進行++操做,從新寫入到主存中,而致使出現num的結果不合預期的狀況。
線程間還能夠實現通訊,這裏不作介紹。
Java中的對象使用new操做符建立,若建立大量短生命週期的對象,則性能低下。因此纔有了池的技術,如數據庫鏈接有鏈接池,線程則有線程池。
使用線程池建立對象的時間是0毫秒,說明其高效性。你們感興趣的可自行查看、瞭解該塊的知識點。
好了,以上歸納的就是多線程的基本知識點了,但願幫到你們。