Java并發(fā)編程進階之線程控制篇
一、線程的基本概念
1.并行和并發(fā)
并行:多個CPU核心同時工作,處理不同的任務。
并發(fā):多個任務交替使用 CPU 核心工作,以提高 CPU 利用率。
2.進程和線程
進程:程序的一次執(zhí)行。由操作系統(tǒng)創(chuàng)建并分配資源,執(zhí)行一個單獨的任務。
進程是系統(tǒng)進行資源分配和調度的獨立單位,每個進程都有自己的內存空間和系統(tǒng)資源。進程內所有線程共享堆存儲空間,保存程序中定義的對象和常量池。
Windows系統(tǒng)中,每個運行的 Java 程序都是一個獨立的進程。
線程:進程內的執(zhí)行單元,不分配單獨的資源,執(zhí)行一個單獨的子任務。
線程是進程內調度和分派的基本單位,共享進程資源。每個線程有自己的獨立的棧存儲空間,保存線程執(zhí)行的方法以及基本類型的數(shù)據(jù)。
運行的 Java 程序內含至少一個主線程 main ,用戶可以在 Java 程序中自定義并調用多個線程。 JVM 垃圾回收線程也是一個獨立的線程。

二、線程的運行狀態(tài)
線程除創(chuàng)建狀態(tài) New 和結束狀態(tài) Terminate 外,主要有以下幾種運行狀態(tài):
①運行(Running) :CPU 正在執(zhí)行線程。
②就緒(Runnable) :線程一切就緒,等待 CPU 執(zhí)行。
運行/就緒狀態(tài) 統(tǒng)稱為可運行狀態(tài) Runnable。 Java 程序中,線程在 運行/就緒狀態(tài) 之間的切換由 JVM 自動調度,開發(fā)者無法獲知。線程之間的調度采用分優(yōu)先級多隊列時間片輪轉算法。進程在執(zhí)行完 CPU 時間片切換到就緒狀態(tài)之前會先保存自己的狀態(tài),下次進入運行狀態(tài)時再重新加載。
③阻塞(Blocked) :線程因缺少其他資源,比如請求資源被上鎖而暫停執(zhí)行。在獲得資源后進入就緒狀態(tài)。
④等待(Waitting) :線程接受了等待指令,釋放資源暫停執(zhí)行。在超時/接受喚醒指令后進入就緒狀態(tài)。

三、線程操作實踐
Runnable 接口內唯一聲明了 run 方法,由 Thread 類實現(xiàn)。開發(fā)者在 run 方法中定義運行時線程將要執(zhí)行的功能,線程開啟后由Java虛擬機自動調用并執(zhí)行。如果開發(fā)者主動調用 run 方法,則只會當作普通方法執(zhí)行。
1.線程兩種定義方法
①繼承 Thread 類,重寫 run 方法。
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}②實現(xiàn) Runnable 接口,實現(xiàn) run 方法。推薦使用,避免了單繼承的局限性。
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}2.啟動線程
Thread 類定義了 start 方法。調用 start 方法后,系統(tǒng)會開啟一個新線程進入就緒狀態(tài):由 JVM 會自動對線程進行調度,在運行時調用并執(zhí)行線程的 run 方法。一個線程只能啟動一次。
①如果自定義線程類繼承 Thread 類,直接啟動。
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread("ThreadName");
t1.start();
t2.start();
}
}②如果自定義線程類實現(xiàn) Runnable 接口,則需要借助 Thread 類啟動線程。
public class Main {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t1 = new Thread(mythread); // 由系統(tǒng)指定默認線程名 Thread-X
Thread t2 = new Thread(mythread, "ThreadName"); // 開發(fā)者自定義線程名
t1.start();
t2.start();
}
}3.同時定義和啟動線程
通過匿名內部類方式,我們可以實現(xiàn)同時定義和啟動線程的簡潔寫法。
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable(){
public void run(){
System.out.println(Thread.currentThread().getName());
}
}).start();
}
}
class Test {
public void method() {
System.out.println(Thread.currentThread().getName());
}
}4.線程彈出與暫停
Thread 類定義了 yield 方法。當前線程執(zhí)行到 Thread.yield() 方法,會停止運行進入就緒狀態(tài)。但線程切換到就緒狀態(tài)后,什么時候被 JVM 調度回運行狀態(tài)開發(fā)者無法控制。
public class ThreadDemo {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t1 = new Thread(mythread);
Thread t2 = new Thread(mythread);
t1.start();
t2.start();
}
static class MyThread implements Runnable {
@Override
public void run() {
int count = 0;
for (int i = 0; i < 10000; i++) {
Thread.yield(); // 切換到就緒狀態(tài)
count++;
System.out.println(count);
}
}
}
}Thread 類定義了 sleep 方法。當前線程執(zhí)行到 Thread.sleep(1000) 方法,會停止運行進入阻塞狀態(tài),但仍會保持對象鎖,其他線程不可訪問其資源。直到超時后進入就緒狀態(tài)。調用 sleep 方法需要捕獲或拋出 InterruptException 異常。
public class ThreadDemo {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t1 = new Thread(mythread);
Thread t2 = new Thread(mythread);
t1.start();
t2.start();
}
static class MyThread implements Runnable {
@Override
public void run() {
int count = 0;
for (int i = 0; i < 10000; i++) {
try{
Thread.sleep(1000); // 當前線程暫停 1s
} catch(InterruptException e){
e.printStackTrace();
}
count++;
System.out.println(count);
}
}
}
}5.線程等待與喚醒
①線程等待
當前線程執(zhí)行 obj.wait() 方法,線程會停止運行并釋放對象鎖 obj,其他線程可以訪問其資源。同時線程進入 obj 對象的等待池,直到被 notify 方法喚醒進入就緒狀態(tài)。調用 wait 方法需要捕獲或拋出 InterruptException 異常。
wait 方法允許計時等待。當前線程執(zhí)行 obj.wait(1000) 方法,計時結束后線程會被自動喚醒進入就緒狀態(tài)。
②線程喚醒
當前線程執(zhí)行 obj.notify() 方法,會隨機從 obj 對象等待池中選擇一個線程喚醒,使其進入就緒狀態(tài)。但是 notify 方法不會釋放當前進程的對象鎖,如果該線程持有 obj 對象的鎖,當前線程釋放鎖后被喚醒的其他線程才能被執(zhí)行。如果想被喚醒線程先執(zhí)行,notify 方法后添加 wait 方法釋放鎖。
當前線程執(zhí)行 obj.notifyall() 方法,會將所有 obj 對象等待池中所有線程喚醒。
public class ThreadDemo {
public static void main(String[] args) {
MyThread t = new MyThread("t");
synchronized(t) { // 對 t 設置對象鎖
try {
t.start();
System.out.println("1");
t.wait(); // 當前線程釋放 t 鎖,進入 t 對象等待池
System.out.println("4");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyThread extends Thread {
@Override
public void run() {
synchronized (this) { // 對 t 設置對象鎖
Thread.sleep(1000);
System.out.println("2");
this.notify(); // 隨機喚醒一個 t 對象等待池中的線程
System.out.println("3");
}
}
}
}6.線程中斷
調用 t.stop() 方法可以強制終止線程 t 運行,但強制中斷線程可能會造成意想不到的問題,已不推薦使用。
目前主要采用設置線程中斷標志的方式,向線程發(fā)送中止信號。由線程自行終止運行:
- 執(zhí)行
t.interrupt()方法,將線程 t 中斷標志設為 true 。 - 執(zhí)行
t.isInterrupted()方法,查看線程 t 中斷標志。 - 執(zhí)行
t.interrupted()方法,查看線程 t 中斷標志然后將其設為 false 。
public class ThreadDemo {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t = new Thread(mythread);
t.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
t.interrupt(); // 設置中斷標志為 true
}
static class MyThread implements Runnable {
@Override
public void run() {
while (true) {
System.out.print("hello");
if(this.isInterrupted()){ // 查看中斷標志,若為 true 結束循環(huán)
break;
}
}
}
}
}到此這篇關于Java并發(fā)編程進階之線程控制篇的文章就介紹到這了,更多相關Java線程控制內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
springboot集成redis哨兵集群的實現(xiàn)示例
本文主要介紹了springboot集成redis哨兵集群的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-08-08
在已經使用mybatis的項目里引入mybatis-plus,結果不能共存的解決
這篇文章主要介紹了在已經使用mybatis的項目里引入mybatis-plus,結果不能共存的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
java連接SQL?Server數(shù)據(jù)庫的超詳細教程
最近在java連接SQL數(shù)據(jù)庫時會出現(xiàn)一些問題,所以這篇文章主要給大家介紹了關于java連接SQL?Server數(shù)據(jù)庫的超詳細教程,文中通過圖文介紹的非常詳細,需要的朋友可以參考下2022-06-06
PowerJobAutoConfiguration自動配置源碼流程解析
這篇文章主要為大家介紹了PowerJobAutoConfiguration自動配置源碼流程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12
Spring Boot整合消息隊列RabbitMQ的實現(xiàn)示例
本文主要介紹了Spring Boot整合消息隊列RabbitMQ的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-03-03

