1、線程創(chuàng)建
創(chuàng)建線程有三種方式
繼承Thread類
①定義Thread類的子類剧罩,并重寫該類的run方法栓拜,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱為執(zhí)行體惠昔。
②創(chuàng)建Thread子類的實例幕与,即創(chuàng)建了線程對象。
③調用線程對象的start()方法來啟動該線程镇防。實現(xiàn)Runnable接口
①定義runnable接口的實現(xiàn)類啦鸣,并重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執(zhí)行體来氧。
②創(chuàng)建 Runnable實現(xiàn)類的實例诫给,并依此實例作為Thread的target來創(chuàng)建Thread對象,該Thread對象才是真正的線程對象啦扬。
③調用線程對象的start()方法來啟動該線程中狂。實現(xiàn)Callable接口(jdk1.5新增,在java.util.concurrent包)
①創(chuàng)建Callable接口的實現(xiàn)類扑毡,并實現(xiàn)call()方法胃榕,該call()方法將作為線程執(zhí)行體,并且有返回值瞄摊。
②創(chuàng)建Callable實現(xiàn)類的實例勋又,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值换帜。
③使用FutureTask對象作為Thread對象的target創(chuàng)建并啟動新線程赐写。
④調用FutureTask對象的get()方法來獲得子線程執(zhí)行結束后的返回值
示例代碼
public class ThreadLearning {
//------------------------繼承Thread類-----------------------
//匿名內部類的形式
@org.junit.Test
public void generateThread_1() {
Thread thread = new Thread() {
@Override
public void run() {
//do something
System.out.println("1");
}
};
thread.start();
}
//正常形式
@org.junit.Test
public void generateThread_2() {
Thread1 thread1 = new Thread1();
thread1.start();
}
class Thread1 extends Thread{
@Override
public void run() {
//do something
System.out.println("2");
}
}
//------------------------實現(xiàn)Runnable接口-----------------------
//匿名內部類的形式
@org.junit.Test
public void generateThread_3() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//do something
System.out.println("1");
}
});
thread.start();
}
//正常形式
@org.junit.Test
public void generateThread_4() {
Thread2 thread2 = new Thread2();
Thread thread = new Thread(thread2);
thread.start();
}
class Thread2 implements Runnable{
@Override
public void run() {
//do something
System.out.println("2");
}
}
//------------------------實現(xiàn)Callable接口-----------------------
@org.junit.Test
public void generateThread_5() {
Thread3 thread3 = new Thread3();
FutureTask<String> futureTask = new FutureTask<String>(thread3);
new Thread(futureTask).start();
try {
String s = futureTask.get();
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
class Thread3 implements Callable<String>{
@Override
public String call() throws Exception {
return "我是實現(xiàn)Callable接口的線程類";
}
}
}
2、線程狀態(tài)分類
/**
* 一個線程在給定的某個時刻膜赃,只能處在下面的6中狀態(tài)中的一種挺邀。
*
* 這些狀態(tài)是相對于虛擬機而言的,并不能反映出任何操作系統(tǒng)中線程狀態(tài)跳座。
*
*/
public enum State {
/**
*
* 新建狀態(tài)
*
* 創(chuàng)建后還沒有啟動的線程處在NEW的狀態(tài)端铛;
*
* 而啟動線程只有start()方法。也就是說還未調用start()方法的線程處在NEW的狀態(tài)
*/
NEW,
/**
*
* 運行狀態(tài)
*
* 處在此狀態(tài)的線程有可能正在運行疲眷,也有可能正在等待CPU為它分配執(zhí)行時間
* 對于虛擬機而言禾蚕,處在RUNNABLE狀態(tài)的線程就是正在執(zhí)行。
*
*/
RUNNABLE,
/**
*
* 阻塞狀態(tài)
*
* 處在阻塞狀態(tài)的線程正在等待一個鎖
*
* 在程序等待進入同步區(qū)域的時候狂丝,線程將進入這種狀態(tài)换淆。
*
*
*/
BLOCKED,
/**
*
* 無限期等待狀態(tài)
*
* 線程進入無限期等待狀態(tài)的原因是調用了下面三種方法之一:
* ①沒有設置Timeout參數(shù)的Object.wait()方法
* ②沒有設置Timeout參數(shù)的Thread.join()方法
* ③LockSupport.park()方法
*
*
*/
WAITING,
/**
*
* 限期等待狀態(tài)
*
* 線程進入限期等待狀態(tài)的原因是調用了下面五種方法之一:
*
* ①設置Timeout參數(shù)的Object.wait()方法
* ②設置Timeout參數(shù)的Thread.join()方法
* ③LockSupport.parkNanos()方法
* ④LockSupport.parkUntil
* ⑤Thread.sleep()方法
*
*/
TIMED_WAITING,
/**
*
* 結束狀態(tài)
*
* 線程已經(jīng)完成執(zhí)行哗总。
*/
TERMINATED;
}
3、狀態(tài)切換圖
4倍试、各個方法的解釋
先看屬于線程Thread的方法
public static native void yield();
該方法向線程調度器提示讯屈,當前線程(調用該方法的線程對象代表的線程)愿意讓出處理器的使用權,但是線程調度器可能會忽視該提示县习。
很少有適合使用該方法的地方涮母。它可能在調試或者測試的時候,幫助重現(xiàn)由于競爭條件出現(xiàn)的bug躁愿。它還可能在設計并發(fā)控制結構的時候提供幫助叛本,比如java.util.concurrent.locks包中的一些類。
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException;
該方法會讓當前線程(調用該方法的線程對象代表的線程)睡眠指定的一段時間(暫時停止執(zhí)行)彤钟,但當前線程不會失去鎖的擁有權(還會繼續(xù)占用鎖)来候。
上述兩個方法只是指定的睡眠時間精度不同。
public synchronized void start()
該方法會讓當前線程(調用該方法的線程對象代表的線程)開始執(zhí)行逸雹。JVM虛擬機會調用該線程的run()方法吠勘。對一個線程只能start()一次。
public final synchronized void join(long millis) throws InterruptedException
public final synchronized void join(long millis, int nanos) throws InterruptedException
public final void join() throws InterruptedException
該方法會讓當前線程(調用該方法的線程峡眶,注意剧防,這里并不是thread.join()
中thread所表示的線程,而是方法thread.join()
所在的線程)等待指定時間(如果為0辫樱,表示一直等到thread執(zhí)行完)峭拘,讓join()方法的所屬線程thread執(zhí)行。
join()方法內部是調用wait()方法實現(xiàn)的狮暑。
再看Object中的方法
public final native void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
public final void wait() throws InterruptedException
該方法會讓當前線程(該方法調用所在的線程)進入等待狀態(tài)鸡挠,直到被該對象(wait()
方法的this
對象)的notify()
或者notifyAll()
喚醒,或者等待時間結束(wait(long timeout)
中的timeout
結束)搬男。
當前線程必須擁有該對象的monitor拣展。
當前線程會將自己放置在等待集中(該對象的等待集),并放棄所有對該對象的同步聲明缔逛。該線程就會不可用并保持休眠备埃,直到發(fā)生下面的4種事件中的任意一件:
①其他線程觸發(fā)了該對象的notify方法,那么該線程就可能會被喚醒(并不一定會喚醒褐奴,可能很多線程都wait該對象)按脚;
②其他線程觸發(fā)了notifyAll方法;
③被其他線程中斷(interrupt()方法)
④設定的等待時間已到敦冬。如果是wait(0)
或者wait()
則會一直等待直到喚醒辅搬。
上述事件之一發(fā)生后,該線程就會從該對象的等待集中移除脖旱,重新加入線程調度堪遂。此后介蛉,它就會和其他線程一樣去爭奪該對象的鎖,一旦獲取了該對象的控制權溶褪,所有該對象的同步聲明就會重新加載币旧。
正確使用wait()的方式如下:(等待應該總是發(fā)生在輪詢中:waits should always occur in loops
)
synchronized (obj) {
while (condition does not hold)
obj.wait(timeout);
... // Perform action appropriate to condition
}
如果當前線程在等待之前或者等待期間被其他線程打斷(Thread.interrupt()),就會拋出InterruptedException異常竿滨。
該方法只應該被擁有對象monitor的線程調用佳恬。
public final native void notify();
public final native void notifyAll();
notify()
喚醒等待該對象monitor的單個線程捏境。如果多個線程都在等待該對象monitor于游,那么被喚醒的線程是不固定的、任意的垫言。被喚醒的線程并不會立即執(zhí)行贰剥,而是等到當前線程放棄了該對象的鎖。被喚醒的線程會和其他線程去競爭對該對象的同步操作筷频。也就是說蚌成,被喚醒的線程僅僅是被喚醒去參與競爭,并不是喚醒了就開始執(zhí)行凛捏。
該方法只應該被該對象的monitor的所有者線程調用担忧。一個線程成為該對象的monitor的所有者有以下三種途徑:
①執(zhí)行該對象的同步的實例方法
②執(zhí)行一個同步塊,并且該同步了該對象
③對Class類型的對象坯癣,執(zhí)行該類的一個同步的靜態(tài)方法
為什么要使用多線程
①充分利用計算機CPU資源
假如CPU為4核瓶盛,使用一個線程,那這個線程只會在一個CPU核心工作示罗,其他三個CPU核心就浪費了惩猫。
②避免阻塞
在執(zhí)行一件耗時的任務(TASK)的時候(比如下載文件),假如只有一個線程蚜点,就只能等任務執(zhí)行完成才能執(zhí)行另外的任務轧房。如果多線程,只需要讓一個線程去執(zhí)行下載任務绍绘,另一個線程繼續(xù)處理你其他任務奶镶,比如繼續(xù)刷網(wǎng)頁。
③并行處理任務
比如有很多任務需要執(zhí)行(比如10個人同時要下載文件)陪拘,如果只有一個線程实辑,只能逐一去下載,排在第一個的人很高興藻丢,但是后面的人需要等到前面的人下載完才能輪到自己剪撬,會很不樂意。使用多線程(假如有10個線程)悠反,每個線程對應一個下載任務残黑,CPU就會同時(只是看上去同時)執(zhí)行這些線程馍佑,這樣每個人都可以以基本相同的速度下載完文件。
一個實際的應用就是B/S架構的應用梨水,并行處理大量請求拭荤。
④就想到這么多。疫诽。舅世。
內容參考
Java多線程學習(二)---線程創(chuàng)建方式
jdk1.8
《深入理解Java虛擬機》
《Java并發(fā)編程的藝術》