在開始學習Thread以前,咱們先來了解一下 線程和進程之間的關係:html
線程(Thread)是進程的一個實體,是CPU調度和分派的基本單位。 線程不可以獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。 線程和進程的關係是:線程是屬於進程的,線程運行在進程空間內,同一進程所產生的線程共享同一內存空間,當進程退出時該進程所產生的線程都會被強制退出並清除。java
由上描述,能夠得知線程做爲cpu的基本調度單位,只有把多線程用好,才能充分利用cpu的多核資源。git
本文基於JDK 8(也能夠叫JDK 1.8)。github
建立線程有四種方式:api
public class MyThread implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
Thread thread = new Thread(new MyThread()); thread.start();
public class MyThread extends Thread{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
MyThread thread = new MyThread(); thread.start();
以上代碼有更簡單的寫法,以下:多線程
Thread thread = new Thread(){ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; thread.start();
new Thread(()-> System.out.println(Thread.currentThread().getName())).start();
看源碼能夠知道Thread的父類是Runnable是JDK1.0提供的,而Callable和Runnable相似,是JDK1.5提供的,彌補了調用線程沒有返回值的狀況,能夠看作是Runnable的一個補充,下面看看Callable的實現。oracle
public class MyThread implements Callable<String> { @Override public String call() throws Exception { System.out.println(Thread.currentThread().getName()); return Thread.currentThread().getName(); } }
Callable<String> callable = new MyThread(); FutureTask<String> ft = new FutureTask<>(callable); new Thread(ft,"threadName").start(); System.out.println(ft.get());
真正啓動線程的是start()方法而不是run(),run()和普通的成員方法同樣,能夠重複使用,但不能啓動一個新線程。ide
Thread類方法學習
方法 | 說明 |
---|---|
start() | 啓動線程 |
setName(String name) | 設置線程名稱 |
setPriority(int priority) | 設置線程優先級,默認5,取值1-10 |
join(long millisec) | 掛起線程xx毫秒,參數能夠不傳 |
interrupt() | 終止線程 |
isAlive() | 測試線程是否處於活動狀態 |
Thread靜態(static)方法測試
方法 | 說明 |
---|---|
yield() | 暫停當前正在執行的線程對象,並執行其餘線程。 |
sleep(long millisec)/sleep(long millis, int nanos) | 掛起線程xx秒,參數不可省略 |
currentThread() | 返回對當前正在執行的線程對象的引用 |
holdsLock(Object x) | 當前線程是否擁有鎖 |
sleep爲線程的方法,而wait爲Object的方法,他們的功能類似,最大本質的區別是:sleep不釋放鎖,wait釋放鎖。
用法上的不一樣:sleep(milliseconds)能夠用時間指定來使他自動醒過來,若是時間不到你只能調用interreput()來終止線程;wait()能夠用notify()/notifyAll()直接喚起。
重點: 測試wait和sleep釋放鎖的代碼以下:
public class SynchronizedTest extends Thread { int number = 10; public synchronized void first(){ System.out.println("this is first!"); number = number+1; } public synchronized void secord() throws InterruptedException { System.out.println("this is secord!!"); Thread.sleep(1000); // this.wait(1000); number = number*100; } @Override public void run() { first(); } }
SynchronizedTest synchronizedTest = new SynchronizedTest(); synchronizedTest.start(); synchronizedTest.secord(); // 主線程稍等10毫秒 Thread.sleep(10); System.out.println(synchronizedTest.number);
根據結果能夠得知:
總結: 使用 sleep(1000)不釋放同步鎖,執行的是10*100+1=1001,wait(1000)釋放了鎖,執行的順序是(10+1)x100=1100,因此sleep不釋放鎖,wait釋放鎖。
線程狀態:
線程的狀態可使用getState()查看,更多狀態詳情,查看Thread源碼,以下圖:
Thread thread = new Thread() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; // 只聲明不調用start()方法,獲得的狀態是NEW System.out.println(thread.getState()); // NEW
Thread thread = new Thread() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; thread.start(); System.out.println(thread.getState()); // RUNNABLE
使用synchronized同步阻塞實現,代碼以下:
public class MyCounter { int counter; public synchronized void increase() { counter++; try { Thread.sleep(10*1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
MyCounter myCounter = new MyCounter(); // 線程1調用同步線程,模擬阻塞 new Thread(()-> myCounter.increase()).start(); // 線程2繼續調用同步阻塞方法 Thread thread = new Thread(()-> myCounter.increase()); thread.start(); // 讓主線程等10毫秒 Thread.currentThread().sleep(10); // 打印線程2,爲阻塞狀態:BLOCKED System.out.println(thread.getState());
public class MyThread extends Thread{ @Override public void run() { synchronized (MyThread.class){ try { MyThread.class.wait(); System.out.println(Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Thread thread = new Thread(new MyThread()); thread.start(); // 主線程掛起200毫秒,等thread執行完成 Thread.sleep(200); // 輸出WAITING,線程thread一直處於被掛起狀態 System.out.println(thread.getState());
喚醒線程: 可以使用 notify/notifyAll 方法,代碼以下:
synchronized (MyThread.class) { MyThread.class.notify(); }
使線程WAITING的方法:
查看Thread源碼能夠知道Thread的join方法,底層使用的是Object的wait實現的,以下圖:
注意: 查看Object的源碼可知wait(),不傳遞參數,等同於wait(0),設置的「0」不是當即執行,而是無限的等待,不執行,以下圖:
TIMED_WAITING狀態,只須要給wait設置上時間便可,代碼以下:
public class MyThread extends Thread{ @Override public void run() { synchronized (MyThread.class){ try { MyThread.class.wait(1000); System.out.println(Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
調用代碼仍是同樣的,以下:
Thread thread = new Thread(new MyThread()); thread.start(); // 主線程掛起200毫秒,等thread執行完成 Thread.sleep(200); // 輸出TIMED_WAITING System.out.println(thread.getState()); synchronized (MyThread.class) { MyThread.class.notify(); }
Thread thread = new Thread(()-> System.out.println(Thread.currentThread().getName())); thread.start(); // 讓主線程等10毫秒 Thread.currentThread().sleep(10); System.out.println(thread.getState());
根據前面的知識,咱們知道使用sleep的時候是不釋放鎖的,因此利用這個特性咱們能夠很輕易的寫出死鎖的代碼,具體的流程如圖(圖片來源於楊曉峯老師文章):
代碼以下:
static Object object1 = new Object(); static Object object2 = new Object(); public static void main(String[] args) { Thread thread = new Thread(){ @Override public void run() { synchronized (object1){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (object2){ System.out.println(Thread.currentThread().getName()); } } } }; Thread thread2 = new Thread(){ @Override public void run() { synchronized (object2){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (object1){ System.out.println(Thread.currentThread().getName()); } } } }; thread.start(); thread2.start();
運行上面的代碼,程序會處於無限等待之中。
根據上面的內容,咱們已經系統的學習Thread的使用了,然而學而不思則罔,最後留一個思考題:根據本文介紹的知識,怎麼能避免死鎖?(哈哈,賣個關子,根據文章的知識點組合能夠得出答案)
源碼下載:https://github.com/vipstone/java-core-example.git
推薦部分
本人最近看了前Oracle首席工程師楊曉峯的課程,也是第四部分引用的流程圖的主人,感受很不錯,推薦給你,一塊兒來系統的學習Java核心吧。
參考文檔
https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.State.html