Java Thread

1,線程的基本概念java

線程是一個程序內部的順序控制流。多線程

線程和進程的區別:(資源分配和處理器分配的基本單元)ide

①,每一個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷;
②,線程能夠當作是輕量級進程,同一類線程共享代碼和數據空間,每一個線程有獨立的運行棧和程序計數器(PC),線程切換的開銷小;
③,多進程:操做系統中能同時運行多個任務(程序);
④,多線程:在同一應用程序中有多個順序流同時執行;

Java線程是經過java.lang.Thread類來實現的。VM啓動時會有一個由主方法(public static void main(){})所定義的線程。能夠經過建立Thread的實例來建立新的線程。每一個線程都是經過某個特定的Thread對象所對應的方法run()來完成其操做的,方法run()稱爲線程體。經過調用Thread類的start()方法來啓動一個線程。
注意:
要注意方法調用和運行線程的區別。方法調用,主程序會等方法運行完成程序再繼續往下走;運行線程:至關於新建一個分支,主程序與線程並行執行。函數

2,線程的建立和啓動this

第一種:定義線程類實現Runnable接口spa

Thread thread = new Thread(target);//target爲Runnable接口類型 操作系統

Runnable中只有一個方法: public void run();用以定義線程運行體。 線程

使用Runnable接口能夠爲多個線程提供共享的數據。在實現Runnable接口的類的run方法定義中可使用Thread的靜態方法: 調試

public static Thread currentThread()獲取當前線程的引用。code

例如:

public static void main(String[] args) { Thread t = new Thread(new MyThread()); t.start();//線程啓動
    t.run();//方法調用
} class MyThread implements Runnable { public void run() { ... } }

第二種:定義一個Thread的子類,並重寫其run方法,如:

    class MyThread extends Thread {
        public void run() {...}
    }
而後生成該類的對象:MyThread thread = new MyThread(...);

例如:

public static void main(String[] args) { Thread t = new MyThread(); t.start(); } class MyThread extends Thread { public void run() { ... } }

3,線程的狀態

3.1,線程的狀態類型:

①,新建狀態(New):建立了一個線程對象,它僅僅做爲一個對象實例存在,JVM沒有爲其分配CPU時間片等線程運行資源; ②,就緒狀態(Runnable):在處於建立狀態的線程中調用start方法將線程的狀態轉換爲就緒狀態。這時,線程已經獲得除CPU時間以外的其它系統資源,只等JVM的線程調度器按照線程的優先級對該線程進行調度,從而使該線程擁有可以得到CPU時間片的機會; ③,運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼; ④,阻塞狀態(Blocked):阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。阻塞的狀況分三種: (1),等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池(waitting queue)中; (2),同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池(lock pool)中; (3),其餘阻塞:運行的線程執行sleep()或join()方法,或者發出I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超市、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入就緒狀態; ⑤,掛起狀態(Suspend):能夠經過調用suspend方法(已過期)將線程的狀態轉換爲掛起狀態。這時,線程將釋放佔用的全部資源,由JVM調度轉入臨時存儲空間,直至應用程序調用resume方法(已過期)恢復線程運行; ⑥,死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

3.2,線程狀態轉換圖


3.3,阻塞和掛起的區別:

操做系統中掛起和阻塞的區別以下: (1),掛起是一種主動行爲,所以恢復也應該要主動完成,而阻塞則是一種被動行爲,是在等待事件或資源時任務的表現,你不知道他何時被阻塞(pend),也就不能確切 的知道他何時恢復阻塞。並且掛起隊列在操做系統裏能夠當作一個,而阻塞隊列則是不一樣的事件或資源(如信號量)就有本身的隊列; (2),阻塞(pend)就是任務釋放CPU,其餘任務能夠運行,通常在等待某種資源或信號量的時候出現。掛起(suspend)不釋放CPU,若是任務優先級高就永遠輪不到其餘任務運行,通常掛起用於程序調試中的條件中斷,當出現某個條件的狀況下掛起,而後進行單步調試; (3),阻塞(pend)是task主動去等一個事件或消息,suspend是直接懸掛task,之後這個task和你沒任何關係,任何task間的通訊或者同步都和這個suspended task沒任何關係了,除非你resume task; (4),任務調度是操做系統來實現的,任務調度時,直接忽略掛起狀態的任務,可是會顧及處於pend下的任務,當pend下的任務等待的資源就緒後,就能夠轉爲ready了。ready只須要等待CPU時間,固然,任務調度也佔用開銷,可是不大,能夠忽略。能夠這樣理解,只要是掛起狀態,操做系統就不在管理這個任務了; (5),掛起是主動的,通常須要用掛起函數進行操做,若沒有resume的動做,則此任務一直不會ready。而阻塞是由於資源被其餘任務搶佔而處於休眠態。二者的表現方式都是從就緒態裏「清掉」,即對應標誌位清零,只不過實現方式不同。 

3.4,wait和sleep 的區別

(1),這兩個方法來自不一樣的類分別是,sleep來自Thread類,和wait來自Object類。sleep是Thread的靜態類方法,誰調用的誰去睡覺,即便在a線程裏調用了b的sleep方法,實際上仍是a去睡覺,要讓b線程睡覺要在b的代碼中調用sleep。 (2),最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其餘線程可使用同步控制塊或者方法。sleep不出讓系統資源;wait是進入線程等待池等待,出讓系統資源,其餘線程能夠佔用CPU。通常wait不會加時間限制,由於若是wait線程的運行資源不夠,再出來也沒用,要等待其餘線程調用notify/notifyAll喚醒等待池中的全部線程,纔會進入就緒隊列等待OS分配系統資源。sleep(milliseconds)能夠用時間指定使它自動喚醒過來,若是時間不到只能調用interrupt()強行打斷。Thread.Sleep(0)的做用是「觸發操做系統馬上從新進行一次CPU競爭」。 (3),使用範圍:wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep能夠在任何地方使用 synchronized(x){ x.notify() //或者wait()
 } (4),sleep必須捕獲異常,而wait,notify和notifyAll不須要捕獲異常

3.5,線程控制的基本方法

 isAlive() 判斷線程是否還「活着」,即線程是否還未終止
 getPriority() 得到線程的優先級數值
 setPriority(int newPriority) 設置線程的優先級數值

 sleep(long millis)

 sleep(long millis, int nanos)

將當前線程睡眠指定時間
 join() 調用某線程的該方法,將當前線程與該線程「合併」,即等待該線程結束,再恢復當前線程的運行
 yield() 讓出CPU,當前線程進入就緒隊列等待調度
 wait() 當前線程進入線程等待池(wait pool)

 notify()

 notifyAll()

喚醒對象的wait pool中的一個/全部等待線程

 

3.5.1,線程優先級

Java提供一個線程調度器來監控程序中就緒狀態的全部線程。線程調度器按照線程的優先級決定應調度哪一個線程來執行。線程的優先級用數字標誌,範圍1~10,缺省優先級爲5

public static final int MIN_PRIORITY = 1; public static final int NORM_PRIORITY = 5; public static final int MAX_PRIORITY = 10; 使用如下方法得到或這隻線程對象的優先級: public final void setPriority(int paramInt) { checkAccess(); if ((paramInt > 10) || (paramInt < 1)) { throw new IllegalArgumentException(); } ThreadGroup localThreadGroup; if ((localThreadGroup = getThreadGroup()) != null) { if (paramInt > localThreadGroup.getMaxPriority()) { paramInt = localThreadGroup.getMaxPriority(); } setPriority0(this.priority = paramInt); } } public final int getPriority() { return this.priority; }

3.5.2,sleep/join/yield方法

(1),sleep:能夠調用Thread的靜態方法sleep使得當前線程休眠,因爲是靜態方法,sleep能夠直接由類名直接調用。

例子:

public class Test { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); try { Thread.sleep(10000);//主線程睡眠 
        } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt();//打斷子線程(不推薦的方法,還有stop()) //thread.flag = false;
 } } class MyThread extends Thread { boolean flag = true; public void run() { while(flag) { System.out.println("====" + new Date() + "===="); try { sleep(1000); } catch (InterruptedException e) { return;//sleep時出現異常則線程直接結束
 } } } }

結果:

====Tue Jun 14 11:05:30 CST 2016====
====Tue Jun 14 11:05:31 CST 2016====
====Tue Jun 14 11:05:32 CST 2016====
====Tue Jun 14 11:05:33 CST 2016====
====Tue Jun 14 11:05:34 CST 2016====
====Tue Jun 14 11:05:35 CST 2016====
====Tue Jun 14 11:05:36 CST 2016====
====Tue Jun 14 11:05:37 CST 2016====
====Tue Jun 14 11:05:38 CST 2016====
====Tue Jun 14 11:05:39 CST 2016====

源碼:

public static native void sleep(long paramLong) throws InterruptedException; public static void sleep(long paramLong, int paramInt) throws InterruptedException { if (paramLong < 0L) { throw new IllegalArgumentException("timeout value is negative"); } if ((paramInt < 0) || (paramInt > 999999)) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if ((paramInt >= 500000) || ((paramInt != 0) && (paramLong == 0L))) { paramLong += 1L; } sleep(paramLong); }

(2),join:合併線程

public class Test { public static void main(String[] args) { MyThread thread = new MyThread("abcde"); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 3; i++) { System.out.println("I am main thread"); } } } class MyThread extends Thread { MyThread(String name) { super(name); } public void run() { for (int i = 0; i < 3; i++) { System.out.println("I am " + getName()); } } }

結果:

I am abcde
I am abcde
I am abcde
I am main thread
I am main thread
I am main thread

源碼:

public final void join() throws InterruptedException { join(0L); } public final synchronized void join(long paramLong) throws InterruptedException { long l1 = System.currentTimeMillis(); long l2 = 0L; if (paramLong < 0L) { throw new IllegalArgumentException("timeout value is negative"); } if (paramLong == 0L) { while (isAlive()) { wait(0L); } } while (isAlive()) { long l3 = paramLong - l2; if (l3 <= 0L) { break; } wait(l3); l2 = System.currentTimeMillis() - l1; } }

(3),yield:讓出CPU,給其餘線程執行的機會

例子:

public class Test { public static void main(String[] args) { MyThread t1 = new MyThread("t1"); MyThread t2 = new MyThread("t2"); t1.start(); t2.start(); } } class MyThread extends Thread { MyThread(String name) { super(name); } public void run() { for (int i = 1; i <= 100; i++) { System.out.println(getName() + ":" + i); if(i%10 == 0) { yield(); } } } }

結果:

每當被10整除的時候會yield ... t1:14 t2:70 t1:15 ... ... t2:72 t1:20 t2:73 ... t2:76 t1:30 t2:77 ...

源碼:

public static native void yield();

4,線程同步

在Java語言中,引入了對象互斥鎖的概念,保證共享數據錯作的完整性,每一個對象對應於一個可稱爲」互斥鎖「的標記,這個標記保證在任一時刻,只能有一個線程訪問該對象。
關鍵字synchronized來與對象的互斥鎖聯繫。當某個對象synchronized修飾時,代表該對象在任一時刻只能由一個線程訪問。
例一:

public class Test implements Runnable { Timer timer = new Timer(); public static void main(String[] args) { Test test = new Test(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.setName("t1"); t2.setName("t2"); t1.start(); t2.start(); } @Override public void run() { timer.add(Thread.currentThread().getName()); } } class Timer { private static int num = 0; public synchronized void add(String name) { // synchronized (this) {
        num++; try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(name + ", 你是第" + num + "個使用timer的線程"); // }
 } }

結果一(可能):

t1, 你是第2個使用timer的線程
t2, 你是第2個使用timer的線程

加上synchronized控制

結果二:

t1, 你是第1個使用timer的線程
t2, 你是第2個使用timer的線程

例二:

public class Test implements Runnable { public int flag = 1; static Object o1 = new Object(); static Object o2 = new Object(); @Override public void run() { System.out.println("flag = " + flag); if(flag == 1) { synchronized (o1) {//得到對象o1鎖 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2) {//得到對象o2鎖 System.out.println(1); } } } if(flag == 0) { synchronized (o2) {//得到對象o2鎖 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1) {//得到對象o1鎖 System.out.println(0); } } } } public static void main(String[] args) { Test test1 = new Test(); Test test2 = new Test(); test1.flag = 1; test2.flag = 0; Thread t1 = new Thread(test1); Thread t2 = new Thread(test2); t1.start(); t2.start(); } }

結果:

flag = 0 flag = 1 分別在等待得到o1,o2對象鎖的,程序死鎖,沒法結束

例三:

哲學家吃飯問題

5,生產者消費者問題

public class Test { public static void main(String[] args) { SyncStack ss = new SyncStack(); Producer p = new Producer(ss); Consumer c = new Consumer(ss); new Thread(c).start(); new Thread(p).start(); new Thread(p).start();//多加個生產者線程
 } } class WoTou { int id; WoTou(int id) { this.id = id; } public String toString() { return "WoTou:" + id; } } class SyncStack { int index = 0; WoTou[] arrWT = new WoTou[5]; //push
    public synchronized void push(WoTou wt) { while(index == arrWT.length) { try { this.wait();//阻塞
            } catch (InterruptedException e) { e.printStackTrace(); } } this.notifyAll();//喚醒其餘全部線程
        arrWT[index] = wt; index++; } //pop
    public synchronized WoTou pop() { while (index == 0) { try { this.wait();//阻塞
            } catch (InterruptedException e) { e.printStackTrace(); } } this.notifyAll();//喚醒其餘全部線程
        index--; return arrWT[index]; } } //生產者
class Producer implements Runnable { SyncStack ss = null; Producer(SyncStack ss) { this.ss = ss; } @Override public void run() { for(int i=1; i<= 10; i++) { WoTou wt = new WoTou(i);//produce
 ss.push(wt); System.out.println("生產了:" + wt); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } //消費者
class Consumer implements Runnable { SyncStack ss = null; Consumer(SyncStack ss) { this.ss = ss; } @Override public void run() { for (int i = 1; i <= 10; i++) { WoTou wt = ss.pop(); System.out.println("消費了:" + wt); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }

結果:

生產了:WoTou:1 消費了:WoTou:1 生產了:WoTou:1 生產了:WoTou:2 消費了:WoTou:2 生產了:WoTou:2 消費了:WoTou:2 生產了:WoTou:3 生產了:WoTou:3 生產了:WoTou:4 生產了:WoTou:4 消費了:WoTou:4 生產了:WoTou:5 消費了:WoTou:5 生產了:WoTou:5 生產了:WoTou:6 消費了:WoTou:5 消費了:WoTou:6 生產了:WoTou:6 生產了:WoTou:7 消費了:WoTou:6 消費了:WoTou:7 生產了:WoTou:8 生產了:WoTou:9 消費了:WoTou:8
相關文章
相關標籤/搜索