Java并發(fā)編程
當(dāng)一個程序啟動后屹逛,操作系統(tǒng)就會為這個程序創(chuàng)建一個進程并分配內(nèi)存空間。如果這個程序是一個Java程序汛骂,那么它的內(nèi)存空間會分為堆區(qū)罕模、棧區(qū)、元數(shù)據(jù)區(qū)帘瞭、本地方法棧淑掌、程序計數(shù)器
等。
當(dāng)進程創(chuàng)建完后蝶念,棧中的主線程也就是main方法就會開始執(zhí)行抛腕。在并發(fā)編程的需求下,需要創(chuàng)建子線程媒殉,首先在堆中創(chuàng)建一個線程對象担敌,然后啟動它。
- 進程适袜,是對運行時程序的封裝柄错,是操作系統(tǒng)進行資源調(diào)度和分配的基本單位舷夺,實現(xiàn)了操作系統(tǒng)的并發(fā)苦酱。
- 線程售貌,是進程的子任務(wù),是CPU調(diào)度和分派的基本單位疫萤,實現(xiàn)了進程內(nèi)部的并發(fā)颂跨。
一、線程的創(chuàng)建
創(chuàng)建Java線程有三種方式
1. 繼承Thread類
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + "第" + i + "次執(zhí)行");
}
}
}
//創(chuàng)建MyThread對象
MyThread t1=new MyThread();
MyThread t2=new MyThread();
//設(shè)置線程的名字
t1.setName("T1線程");
t2.setName("T2線程");
//啟動線程
t1.start();
t2.start();
2. 實現(xiàn)Runnable接口
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {//sleep會發(fā)生異常要顯示處理
Thread.sleep(20);//暫停20毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "第" + i + "次執(zhí)行");
}
}
}
//創(chuàng)建MyRunnable類
MyRunnable mr = new MyRunnable();
//創(chuàng)建Thread類的有參構(gòu)造,并設(shè)置線程名
Thread t1 = new Thread(mr, "T1線程");
Thread t2 = new Thread(mr, "T2線程");
//啟動線程
t1.start();
t2.start();
3. 使用FutureTask類
FutureTask是RunnableFuture的實現(xiàn)類扯饶,RunnableFuture又是Runnable和Future的子接口
FutureTask類的構(gòu)造方法可以傳入Callable接口實現(xiàn)類的實例恒削,也可以傳入Runnable接口的實例與線程執(zhí)行后的返回值
這種創(chuàng)建線程的方法可以在主線程中獲取子線程的返回值以及拋出的異常
public class CallerTask implements Callable<String> {
public String call() throws Exception {
return "task done";
}
}
//創(chuàng)建異步任務(wù)
FutureTask<String> task = new FutureTask<String>(new CallerTask());
//啟動線程
new Thread(task).start();
try {
//等待執(zhí)行完成,并獲取返回結(jié)果
String result = task.get();
//String result = task.get(5,TimeUnit.MINUTES);
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
//通過調(diào)用getCause獲取線程拋出的異常
Throwable cause = e.getCause();
e.printStackTrace();
} catch (TimeoutException e) {
//如果調(diào)用帶有參數(shù)的get方法尾序,當(dāng)線程超時后會拋出此異常
e.printStackTrace();
}
-
run()
方法和start()
方法有什么區(qū)別钓丰?-
run()
:封裝線程執(zhí)行的代碼,直接調(diào)用相當(dāng)于調(diào)用普通方法每币。 -
start()
:啟動線程携丁,然后由JVM 調(diào)用此線程的run()
方法。
-
二兰怠、線程的狀態(tài)
在java.lang.Thread類中的內(nèi)部枚舉梦鉴,定義了Java線程的六種狀態(tài)。
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
1. NEW(新建狀態(tài))
處于NEW狀態(tài)的線程此時尚未啟動揭保。這里的尚未啟動指的是還沒調(diào)用Thread實例的start()方法肥橙。
private void testStateNew() {
Thread thread = new Thread(() -> {});
System.out.println(thread.getState()); // 輸出 NEW
}
2. RUNNABLE(可運行狀態(tài))
Java線程的RUNNABLE狀態(tài)包括了傳統(tǒng)操作系統(tǒng)線程的ready和running兩個狀態(tài)的。
處于RUNNABLE狀態(tài)的線程在Java虛擬機中運行秸侣,也有可能在等待CPU分配資源存筏。
Thread源碼里對RUNNABLE狀態(tài)的定義:
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
//可運行的線程狀態(tài)。處于可運行狀態(tài)的線程正在 Java 虛擬機中執(zhí)行味榛,也可能正在等待來自操作系統(tǒng)(如處理器)的其他資源方篮。
3. BLOCKED(阻塞狀態(tài))
處于BLOCKED狀態(tài)的線程正等待鎖的釋放以進入同步區(qū)。
4. WAITING(等待狀態(tài))
處于等待狀態(tài)的線程變成RUNNABLE狀態(tài)需要其他線程喚醒励负。
調(diào)用如下3個方法會使線程進入等待狀態(tài):
- Object.wait():使當(dāng)前線程處于等待狀態(tài)直到另一個線程喚醒它藕溅;
- Thread.join():等待線程執(zhí)行完畢,底層調(diào)用的是Object實例的wait方法继榆;
- LockSupport.park():除非獲得調(diào)用許可巾表,否則禁用當(dāng)前線程進行線程調(diào)度。
5. TIMED_WAITING(超時等待狀態(tài))
線程等待一個具體的時間略吨,時間到后會被自動喚醒集币。
調(diào)用如下方法會使線程進入超時等待狀態(tài):
- Thread.sleep(long millis):使當(dāng)前線程睡眠指定時間;
- Object.wait(long timeout):線程休眠指定時間翠忠,等待期間可以通過notify()/notifyAll()喚醒鞠苟;
- Thread.join(long millis):等待當(dāng)前線程最多執(zhí)行millis毫秒,如果millis為0,則會一直執(zhí)行当娱;
- LockSupport.parkNanos(long nanos): 除非獲得調(diào)用許可吃既,否則禁用當(dāng)前線程進行線程調(diào)度指定時間;
- LockSupport.parkUntil(long deadline):同上跨细,也是禁止線程進行調(diào)度指定時間鹦倚;
6. TERMINATED(終止?fàn)顟B(tài))
此時線程執(zhí)行完畢。