目錄
- 2 行程與執行緒
- 2-1 應用
- 異步呼叫
- 多執行緒提供程式運行效率
- 2-2 思考
- 2-1 應用
- 3 Java執行緒基本知識點
- 3-1 Java中執行緒的創建方法
- 方法1 (僅使用Thread類,重寫run方法)
- 方法2(使用Thread配合Runable介面使用,推薦)
- 2種方法小結
- 方法3: FutureTask物件配合Thread物件使用(需要執行緒回傳值),
- 3-2 Java執行緒內部原理
- 基本概念:
- Java中執行緒背景關系切換(Thread Context Switch)
- 3-3 執行緒中常用方法
- thread中部分方法:
- interrput 與park結合使用
- 二階段終止模式代碼示例
- 守護執行緒(daemon)
- 3-4 執行緒的狀態
- 從作業系統的角度
- 從Java程式的角度
- 3-5 多執行緒的簡單實體-燒水泡茶
- 3-1 Java中執行緒的創建方法
- 主要參考資料
2 行程與執行緒
行程:可以視為程式的一個實體,有的程式可以開啟多個實體,比如瀏覽器,有的只能一個實體,比如網易云音樂,
執行緒:行程包含多個執行緒,是一個指令流,是JAVA中最小的調度與資源分配單位,在windows中行程是不活動的,只是執行緒的容器,
從資源占用,與通信方法二個角度進行對比,對比行程之間,執行緒之間,行程與執行緒之間,
2-1 應用
異步呼叫
定義:從呼叫方式來說,呼叫分為同步呼叫與異步呼叫,區別在于是否讓主程式等待,
多執行緒提供程式運行效率
2-2 思考
1.單核情況下,多執行緒的本質是讓不同執行緒輪流占用cpu,避免一個執行緒總是占用CPU,其他程式無法干活,
2.多核情況下,多執行緒能夠提高程式運行效率要具體情況具體分析,考慮2點:
- 計算任務是否能夠拆分(阿姆達爾定律)
3.IO操作不占用CPU,但是一般使用阻塞IO:即執行緒雖然不用CPU,但必須等待IO結束,
- 阻塞IO沒能充分利用CPU,所有需要有非阻塞IO和異步IO提高執行緒利用率,
3 Java執行緒基本知識點
3-1 Java中執行緒的創建方法
方法1 (僅使用Thread類,重寫run方法)
package chapter2;
//這個程式要運行必須在IDEA中裝好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j; //匯入注解
@Slf4j(topic = "c.Test1")
/*
new 類名或介面名(){
重寫方法;
}; //注意分號
//以上就是內部類的格式,其實這整體就相當于是new出來的一個物件
本質:其實是繼承該類或者實作介面的子類匿名物件
個人理解:一般我們實作介面或者繼承某個物件的時候會另外在定義一個新的有名稱的類,匿名內部類由于只使用
一次,就沒必要特意的再去重新定義一下,
*/
/*方法1:直接使用Thread*/
public class Test1 {
public static void main(String[] args){ //create the thread
Thread t = new Thread(){
public void run(){
log.warn("running");
}
};
t.setName("thread1");
t.start(); // start the thread
log.warn("running");
}
}
方法2(使用Thread配合Runable介面使用,推薦)
目的:將運行的代碼與執行緒的創建分開
package chapter2;
//這個程式要運行必須在IDEA中裝好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j; //匯入注解
//方法2: Runable物件配合Thread物件使用
@Slf4j(topic = "c.Test2")
public class Test2 {
public static void main(String[] args){ //create the thread
// Runnable r = new Runnable() { //定義匿名類實作Runable()介面
// @Override
// public void run() {
// log.warn("running");
// }
// };
// annoyomous class create(lambda style)
// 定義匿名類實作Runable介面,并使用lamba方式簡化
Runnable r = () -> log.warn("running");
new Thread(r, "thread1").start();
log.warn("running");
}
}
2種方法小結
方法1與方法2的聯系:方法2中的Runable物件會作為引數傳遞給Thread的私有變數target,然后在run方法中被呼叫,本質上2種方法都是對執行緒物件中的run()方法進行修改,
@Override
public void run() {
if (target != null) {
target.run();
}
}
- 使用方法2方便與高級API執行緒池進行配合
- 使用方法2更加靈活,符合JAVA組合優于繼承的思想,
- 可以使用jps查看所有行程,jstack PID查看該行程所有的執行緒
方法3: FutureTask物件配合Thread物件使用(需要執行緒回傳值),
package chapter2;
//這個程式要運行必須在IDEA中裝好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//方法2: Runable物件配合Thread物件使用
@Slf4j(topic = "c.Test2")
public class Test3 {
public static void main(String[] args) throws InterruptedException, ExecutionException { //create the thread
// FutureTask物件能夠回傳行程的執行結果
// 原始碼中FutureTask物件實作了runable介面,因此可以傳遞給Thread物件
FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
log.warn("Runing");
Thread.sleep(1000);
return 100;
}
});
Thread t = new Thread(task,"t1");
t.start();
log.warn("{}", task.get());
}
}
3-2 Java執行緒內部原理
基本概念:
堆疊與堆疊幀:執行緒執行后,被分配一個堆疊記憶體,執行緒之間的堆疊記憶體互不干擾,
- 堆疊幀(frames):每個堆疊記憶體有許多堆疊幀組成,一個堆疊幀對應一次方法呼叫(利用堆疊的特性先進后出實作多次方法呼叫)
- 活動堆疊幀:每個執行緒當前只有一個活動堆疊幀,即當前執行緒執行的方法

Java中執行緒背景關系切換(Thread Context Switch)
何時發生?
- 垃圾回收
- 時間片用完
- 更高優先級執行緒
- 執行緒主動sleep,yield,wait,join,park,synchronized,lock
3-3 執行緒中常用方法
thread中部分方法:
| 方法名 | |
|---|---|
| start()|run() | 創建執行緒并讓執行緒處于就緒態(runable)|執行執行緒代碼,不會創建執行緒 |
| sleep() | 讓執行緒由 running 變為 Time waiting(一定時間內無法被調度) |
| yield() 屈服,讓步; 放棄; 讓路; | 讓執行緒由 running 變為 runable(仍然可被調度) |
| join() | 等待執行緒運行結束(執行緒同步,需要運行的結果) |
| interrupt() | 打斷執行緒(包括阻塞的與正在運行的) |
| 下面3個方法是過時方法 | 容易破壞同步代碼塊,造成死鎖, |
| stop() | 使用2階段終止模式進行替代 |
| suspend() | |
| resume() |
sleep():可以配合interrupt使用,interrupt可以wake up執行緒
yield():放棄CPU的使用權,依賴于CPU的調度器,會出現執行緒yield之后,任務調度器仍然調度該執行緒的情況,
interrupt():
- 中斷執行緒的阻塞狀態(采用sleep(),join(),wait()方法后的執行緒)
- 中斷正常的執行緒,會使得執行緒中斷標記為True
- 結合中斷標記去優雅的停止執行緒
package chapter2;
//這個程式要運行必須在IDEA中裝好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//方法2: Runable物件配合Thread物件使用
@Slf4j(topic = "c.Test2")
public class Test4 {
public static void main(String[] args) throws InterruptedException, ExecutionException { //create the thread
Thread t1 = new Thread(() -> {
while(true){
if(Thread.currentThread().isInterrupted()){ //當前執行緒是否被中斷過
// System.out.println("break the loop");
log.warn("break the loop");
break;
}
}
},"t1");
t1.start();
Thread.sleep(1000); // main執行緒休眠1s
log.warn("interrupt t1");
t1.interrupt(); // 中斷執行緒t1
}
}
運行結果:
[main] WARN c.Test2 - interrupt t1
[t1] WARN c.Test2 - break the loop
Process finished with exit code 0
interrput 與park結合使用
package chapter2;
//這個程式要運行必須在IDEA中裝好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.LockSupport;
//方法2: Runable物件配合Thread物件使用
@Slf4j(topic = "c.Test7")
public class Test7 {
public static void main(String[] args) throws InterruptedException{ //create the thread
log.warn("the main thread!");
parktest();
}
// park處于調度的考慮讓當前執行緒不執行,等待獲取許可證,除非被當前執行緒unpark方法呼叫,
private static void parktest() throws InterruptedException{
Runnable r = ()->{
// 定義匿名類實作Runable介面,由于Runable是函式式介面,因此可以通過lamba進行簡化
log.warn("park...");
LockSupport.park();
log.warn("unpark...");
// interrupted() 方法會查看中斷狀態,將中斷標記重置為假,
// isinterrupted() 方法僅查看中斷狀態
// park之后,用interrupt恢復后,必須恢復中斷標記為假,否則再次park會失效,
log.debug("interrupt state: {}", Thread.interrupted());
};
Thread t1 = new Thread(r,"t1");
t1.start();
t1.sleep(1000);
t1.interrupt(); // 通過interrupt可以恢復park的行程,并將中斷標志變為True
}
}
執行結果:
[main] WARN c.Test7 - the main thread!
[t1] WARN c.Test7 - park...
[t1] WARN c.Test7 - unpark...
注意點:
- park方法能夠讓執行緒停止運行
- park之后,可以用interrupt恢復運行,但必須手動恢復中斷標記為假,否則再次park會失效,
- 區分2個方法interrupt以及isinterrupted
二階段終止模式代碼示例

package chapter2;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutionException;
@Slf4j(topic = "c.Test6")
public class Test6 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
TwoStageTermination tmp = new TwoStageTermination();
tmp.start();
Thread.sleep(5000);
tmp.stop();
}
}
/*
* 每隔2s運行并檢查,可通過外部執行緒殺死該執行緒
* */
@Slf4j(topic = "c.TwoStageTermination")
class TwoStageTermination
{
private Thread monitor;
public void stop(){ /* 正常狀態停止(中斷)監控行程*/
monitor.interrupt(); /* 設定中斷標記為true*/
}
public void start(){
monitor = new Thread(() -> {
while(true){
Thread current = Thread.currentThread();
if(current.isInterrupted()){
log.warn("stop the thread of monitor!");
break;
}
try{
current.sleep(2000);
log.warn("sleep 2 seconds !");
log.warn("check the status...");
}catch(Exception e){
e.printStackTrace();
current.interrupt(); /* 設定中斷標記為true*/
}
}
},"monitor_thread");
monitor.start();
}
}
運行結果:
[monitor_thread] WARN c.TwoStageTermination - sleep 2 seconds !
[monitor_thread] WARN c.TwoStageTermination - check the status...
[monitor_thread] WARN c.TwoStageTermination - sleep 2 seconds !
[monitor_thread] WARN c.TwoStageTermination - check the status...
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at chapter2.TwoStageTermination.lambda$start$0(Test6.java:36)
at java.lang.Thread.run(Thread.java:748)
[monitor_thread] WARN c.TwoStageTermination - stop the thread of monitor!
- 注意點:
- 執行緒在sleep狀態被interrupt會觸發例外,中斷標記為false
- 執行緒運行狀態被interrupt,中斷標記為true,
- 二階段終止模式可以用于過時的stop方法
守護執行緒(daemon)
知識點:
- 通常Java行程會在主執行緒以及其他執行緒運行結束后才會結束,
- 但對于守護執行緒而言,行程結束無需考慮守護執行緒是否已經停止,
- 守護行程的實際例子:
- Java中的垃圾回收執行緒
- Tomcat中的Acceptor以及Poller執行緒都是守護執行緒
3-4 執行緒的狀態
從作業系統的角度
可以劃分為:開始,可執行(就緒),執行,阻塞,結束
從Java程式的角度
在Java的源代碼Thread.java檔案中定義了6種狀態,分別是
NEW:執行緒剛剛被創建時,還沒有start()的狀態
RUNABLE: Java中的RUNABLE包含了作業系統層面的運行,阻塞,可運行狀態,
BLOCKED,WAITING,TIMED_WAITINGJava API層面的阻塞
- TIMED_WAITING:使用sleep方法可能會出現
- WAITING: 使用join方法后可能會出現
- BLOCKED:使用synchronize方法可能會出現
TERMINATED:程式終止狀態

public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* 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.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
3-5 多執行緒的簡單實體-燒水泡茶
場景:

封裝好的Sleeper類
package chapter2;
import java.util.concurrent.TimeUnit;
public class Sleeper {
public static void sleep(int i){
try{
TimeUnit.SECONDS.sleep(i);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
主程式
package chapter2;
//這個程式要運行必須在IDEA中裝好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
//方法2: Runable物件配合Thread物件使用
@Slf4j(topic = "c.Test8")
public class Test8 {
public static void main(String[] args){ //create the thread
Thread t1 = new Thread(()->{
log.warn("洗水壺");
Sleeper.sleep(1);
log.warn("燒開水");
Sleeper.sleep(15);
log.warn("完成洗茶壺和燒開水");
},"老王");
Thread t2 = new Thread(()->{
log.warn("洗茶壺");
Sleeper.sleep(1);
log.warn("洗茶杯");
Sleeper.sleep(2);
log.warn("拿茶葉");
Sleeper.sleep(1);
log.warn("完成洗茶壺和茶杯,拿茶葉");
try{
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.warn("小王泡茶");
},"小王");
t1.start();
t2.start();
}
}
執行結果:
[小王] WARN c.Test8 - 洗茶壺
[老王] WARN c.Test8 - 洗水壺
[老王] WARN c.Test8 - 燒開水
[小王] WARN c.Test8 - 洗茶杯
[小王] WARN c.Test8 - 拿茶葉
[小王] WARN c.Test8 - 完成洗茶壺和茶杯,拿茶葉
[老王] WARN c.Test8 - 完成洗茶壺和燒開水
[小王] WARN c.Test8 - 小王泡茶
總結: 使用2個執行緒進行了統籌規劃,利用join完成最后的同步,
主要參考資料
并發編程課程
2021.2.10
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/258605.html
標籤:Java
下一篇:pom.xml報錯:Failed to read artifact descriptor for xxxxxx.jar問題的解決方法
