日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

老后端被借調(diào)去寫Java了,含淚總結(jié)的Java多線程編程基礎(chǔ)

來源: 責(zé)編: 時(shí)間:2023-12-21 17:10:49 246觀看
導(dǎo)讀這篇文章咱們總結(jié)一下 Java線程的基礎(chǔ),打好基礎(chǔ),后面幾篇再學(xué)多線程的同步控制中的各種鎖、線程通信等方面的知識(shí)時(shí)就會(huì)覺得更容易些。本文的大綱如下:線程在計(jì)算機(jī)系統(tǒng)里每個(gè)進(jìn)程(Process)都代表著一個(gè)運(yùn)行著的程序,比如打

這篇文章咱們總結(jié)一下 Java線程的基礎(chǔ),打好基礎(chǔ),后面幾篇再學(xué)多線程的同步控制中的各種鎖、線程通信等方面的知識(shí)時(shí)就會(huì)覺得更容易些。zQH28資訊網(wǎng)——每日最新資訊28at.com

本文的大綱如下:zQH28資訊網(wǎng)——每日最新資訊28at.com

zQH28資訊網(wǎng)——每日最新資訊28at.com

zQH28資訊網(wǎng)——每日最新資訊28at.com

線程

在計(jì)算機(jī)系統(tǒng)里每個(gè)進(jìn)程(Process)都代表著一個(gè)運(yùn)行著的程序,比如打開微信,系統(tǒng)就會(huì)為微信開一個(gè)進(jìn)程--進(jìn)程是對(duì)運(yùn)行時(shí)程序的封裝,是系統(tǒng)進(jìn)行資源調(diào)度和分配的基本單位。zQH28資訊網(wǎng)——每日最新資訊28at.com

一個(gè)進(jìn)程下可以有很多個(gè)線程,還拿微信舉例子,我們用微信的時(shí)候除了給好友收發(fā)消息,還可以在里面看公眾號(hào),看公眾號(hào)的時(shí)候,也不影響我們的微信收到其他人發(fā)給我們的消息,這就以為著運(yùn)行的微信的進(jìn)程,還開啟了多個(gè)線程來同時(shí)完成這些子任務(wù)。zQH28資訊網(wǎng)——每日最新資訊28at.com

線程是進(jìn)程的子任務(wù),是CPU調(diào)度和分派的基本單位,用于保證程序的實(shí)時(shí)性,實(shí)現(xiàn)進(jìn)程內(nèi)部的并發(fā),線程同時(shí)也是操作系統(tǒng)可識(shí)別的最小執(zhí)行和調(diào)度單位。zQH28資訊網(wǎng)——每日最新資訊28at.com

在 Java 里線程是程序執(zhí)行的載體,我們寫的代碼就是由線程運(yùn)行的。有的時(shí)候?yàn)榱嗽黾映绦虻膱?zhí)行效率,我們不得不使用多線程進(jìn)行編程,雖然多線程能最大化程序利用 CPU 的效率,但是程序用多寫成進(jìn)行任務(wù)處理,也是BUG的高發(fā)地,主要原因還是多線程環(huán)境下有些問題一旦被疏忽就會(huì)造成執(zhí)行結(jié)果不符合預(yù)期的BUG。zQH28資訊網(wǎng)——每日最新資訊28at.com

平時(shí)我們寫代碼思考問題時(shí)的習(xí)慣思維是單線程的,寫多線程的時(shí)候得刻意切換一下才行,這就要求我們要了解清楚線程在不同運(yùn)行條件下所表現(xiàn)出來的行為才行。zQH28資訊網(wǎng)——每日最新資訊28at.com

首先我們來看一下在 Java 中是怎么表示線程的。zQH28資訊網(wǎng)——每日最新資訊28at.com

Java 中的線程

到目前為止,我們寫的所有 Java 程序代碼都是在由JVM給創(chuàng)建的主線程(Main Thread) 中執(zhí)行的。Java 線程就像一個(gè)虛擬 CPU,可以在運(yùn)行的 Java 應(yīng)用程序中執(zhí)行 Java 代碼。當(dāng)一個(gè) Java 應(yīng)用程序啟動(dòng)時(shí),它的入口方法 main() 方法由主線程執(zhí)行。主線程(Main Thread)是一個(gè)由 Java 虛擬機(jī)創(chuàng)建的運(yùn)行應(yīng)用程序的特殊線程。zQH28資訊網(wǎng)——每日最新資訊28at.com

我們?cè)?Java 里萬物皆對(duì)象,所以系統(tǒng)的線程在 Java 里也是用對(duì)象表示的,線程是類 java.lang.Thread 類或者其子類的實(shí)例。在 Java 應(yīng)用程序內(nèi)部, 我們可以通過線程對(duì)象創(chuàng)建和啟動(dòng)更多線程,這些線程可以與主線程并行執(zhí)行應(yīng)用程序的代碼。zQH28資訊網(wǎng)——每日最新資訊28at.com

下面看一下怎么在 Java 程序里創(chuàng)建和啟動(dòng)線程。zQH28資訊網(wǎng)——每日最新資訊28at.com

創(chuàng)建和啟動(dòng)線程

在 Java 中創(chuàng)建一個(gè)線程,就是創(chuàng)建一個(gè)Thread類的實(shí)例zQH28資訊網(wǎng)——每日最新資訊28at.com

 Thread thread = new Thread();

啟動(dòng)線程就是調(diào)用Thread對(duì)象的start()方法zQH28資訊網(wǎng)——每日最新資訊28at.com

  thread.start();

當(dāng)然,這個(gè)例子沒有指定線程要執(zhí)行的代碼,所以線程將在啟動(dòng)后立即停止。 讓線程執(zhí)行邏輯,需要給線程對(duì)象指定執(zhí)行體。zQH28資訊網(wǎng)——每日最新資訊28at.com

指定線程要執(zhí)行的代碼

有兩種方法可以給線程指定要執(zhí)行的代碼。zQH28資訊網(wǎng)——每日最新資訊28at.com

  • 第一種是,創(chuàng)建Thread類的子類,在子類中覆蓋Thread類的run() 方法,在這個(gè)run() 方法的方法體里的代碼,就是指定給線程去執(zhí)行的代碼。
  • 第二種是,將實(shí)現(xiàn) Runnable (java.lang.Runnable) 的對(duì)象傳遞給Thread構(gòu)造方法,創(chuàng)建Thread實(shí)例。

其實(shí),還有第三種給線程指定執(zhí)行代碼的方法,不過細(xì)究下來算是第二種方法的特殊使用方式,下面我們看看這三種指定線程執(zhí)行方法體的方式,以及它們之間的區(qū)別。zQH28資訊網(wǎng)——每日最新資訊28at.com

1.通過 Thread 子類指定要執(zhí)行的代碼

通過繼承Thread類創(chuàng)建線程的步驟:zQH28資訊網(wǎng)——每日最新資訊28at.com

(1) 定義 Thread 類的子類,并覆蓋該類的 run() 方法。run() 方法的方法體就代表了線程要完成的任務(wù),因此把 run() 方法稱為執(zhí)行體。zQH28資訊網(wǎng)——每日最新資訊28at.com

(2) 創(chuàng)建 Thread 子類的實(shí)例,即創(chuàng)建了線程對(duì)象。zQH28資訊網(wǎng)——每日最新資訊28at.com

(3) 調(diào)用線程對(duì)象的 start() 方法來啟動(dòng)該線程。zQH28資訊網(wǎng)——每日最新資訊28at.com

package com.learnthread;public class ThreadFirstRunDemo {    public static void main(String[] args) {        // 實(shí)例化線程對(duì)象        MyThread threadA = new MyFirstThread("線程-A");        MyThread threadB = new MyFirstThread("線程-B");        // 啟動(dòng)線程        threadA.start();        threadB.start();    }    static class MyFirstThread extends Thread {        private int ticket = 5;        MyThread(String name) {            super(name);        }        @Override        public void run() {            while (ticket > 0) {                System.out.println(Thread.currentThread().getName() + " 賣出了第 " + ticket + " 張票");                ticket--;            }        }    }}

上面的程序,主線程啟動(dòng)調(diào)用A、B兩個(gè)線程的start() 后,并沒有通過調(diào)用wait() 等待他們執(zhí)行結(jié)束。A、B兩個(gè)線程的執(zhí)行體,會(huì)并發(fā)地被系統(tǒng)執(zhí)行,等線程都直接結(jié)束后,程序才會(huì)退出。zQH28資訊網(wǎng)——每日最新資訊28at.com

2.通過實(shí)現(xiàn) Runnable 接口指定要執(zhí)行的代碼

Runnable 接口的定義如下,只有一個(gè) run() 方法的定義:zQH28資訊網(wǎng)——每日最新資訊28at.com

package java.lang;public interface Runnable {    public abstract void run();}

其實(shí),Thread 類實(shí)現(xiàn)的也是 Runnable 接口。 在 Thread 類的重載構(gòu)造方法里,支持接收一個(gè)實(shí)現(xiàn)了 Runnale 接口的對(duì)象作為其 target 參數(shù)來初始化線程對(duì)象。zQH28資訊網(wǎng)——每日最新資訊28at.com

public class Thread implements Runnable {        ... public Thread(Runnable target) {        init(null, target, "Thread-" + nextThreadNum(), 0);    }        ...    public Thread(Runnable target, String name) {        init(null, target, name, 0);    }    ...}

通過實(shí)現(xiàn) Runnable 接口創(chuàng)建線程的步驟如下:zQH28資訊網(wǎng)——每日最新資訊28at.com

(1) 定義 Runnable 接口的實(shí)現(xiàn),實(shí)現(xiàn)該接口的 run 方法。該 run 方法的方法體同樣是線程的執(zhí)行體。zQH28資訊網(wǎng)——每日最新資訊28at.com

(2) 創(chuàng)建 Runnable 實(shí)現(xiàn)類的實(shí)例,并以此實(shí)例作為 Thread 的 target 參數(shù)來創(chuàng)建 Thread 對(duì)象,該 Thread 對(duì)象才是真正的線程對(duì)象。zQH28資訊網(wǎng)——每日最新資訊28at.com

(3) 調(diào)用線程對(duì)象的 start 方法來啟動(dòng)線程并執(zhí)行。zQH28資訊網(wǎng)——每日最新資訊28at.com

package com.learnthread;public class RunnableThreadDemo {    public static void main(String[] args) {        // 實(shí)例化線程對(duì)象        Thread threadA = new Thread(new MyThread(), "Runnable 線程-A");        Thread threadB = new Thread(new MyThread(), "Runnable 線程-B");        // 啟動(dòng)線程        threadA.start();        threadB.start();    }    static class MyThread implements Runnable {        private int ticket = 5;        @Override        public void run() {            while (ticket > 0) {                System.out.println(Thread.currentThread().getName() + " 賣出了第 " + ticket + " 張票");                ticket--;            }        }    }}

運(yùn)行上面例程會(huì)有以下輸出,同樣程序會(huì)在所以線程執(zhí)行完后退出。zQH28資訊網(wǎng)——每日最新資訊28at.com

Runnable 線程-B 賣出了第 5 張票Runnable 線程-B 賣出了第 4 張票Runnable 線程-B 賣出了第 3 張票Runnable 線程-B 賣出了第 2 張票Runnable 線程-B 賣出了第 1 張票Runnable 線程-A 賣出了第 5 張票Runnable 線程-A 賣出了第 4 張票Runnable 線程-A 賣出了第 3 張票Runnable 線程-A 賣出了第 2 張票Runnable 線程-A 賣出了第 1 張票Process finished with exit code 0

既然是給 Thread 傳遞 Runnable 接口的實(shí)現(xiàn)對(duì)象即可,那么除了普通的定義類實(shí)現(xiàn)接口的方式,我們還可以使用匿名類和 Lambda 表達(dá)式的方式來定義 Runnable 的實(shí)現(xiàn)。zQH28資訊網(wǎng)——每日最新資訊28at.com

  • 使用 Runnable 的匿名類作為參數(shù)創(chuàng)建 Thread 對(duì)象:
Thread threadA = new Thread(new Runnable() {    private int ticket = 5;    @Override    public void run() {        while (ticket > 0) {            System.out.println(Thread.currentThread().getName() + " 賣出了第 " + ticket + " 張票");            ticket--;        }    }}, "Runnable 線程-A");
  • 使用實(shí)現(xiàn)了 Runnable 的 Lambda 表達(dá)式作為參數(shù)創(chuàng)建 Thread 對(duì)象:
Runnable runnable = () -> { System.out.println("Lambda Runnable running"); };Thread threadB = new Thread(runnable, "Runnable 線程-B");

因?yàn)椋琇ambda 是無狀態(tài)的,定義不了內(nèi)部屬性,這里就舉個(gè)簡(jiǎn)單的打印一行輸出的例子了,理解一下這種用法即可。zQH28資訊網(wǎng)——每日最新資訊28at.com

zQH28資訊網(wǎng)——每日最新資訊28at.com

3.獲取線程的執(zhí)行結(jié)果

上面兩種方法雖然能指定線程執(zhí)行體里要執(zhí)行的任務(wù),但是都沒有返回值,如果想讓線程的執(zhí)行體方法有返回值,且能被外部創(chuàng)建它的父線程獲取到返回值,就需要結(jié)合J.U.C 里提供的 Callable、Future 接口來實(shí)現(xiàn)線程的執(zhí)行體方法才行。zQH28資訊網(wǎng)——每日最新資訊28at.com

J.U.C 是 java.util.concurrent 包的縮寫,提供了很多并發(fā)編程的工具類,后面會(huì)詳細(xì)學(xué)習(xí)。zQH28資訊網(wǎng)——每日最新資訊28at.com

zQH28資訊網(wǎng)——每日最新資訊28at.com

Callable 接口只聲明了一個(gè)方法,這個(gè)方法叫做 call():zQH28資訊網(wǎng)——每日最新資訊28at.com

package java.util.concurrent;public interface Callable<V> {    V call() throws Exception;}

Future 就是對(duì)于具體的 Callable 任務(wù)的執(zhí)行進(jìn)行取消、查詢是否完成、獲取執(zhí)行結(jié)果的。可以通過 get 方法獲取 Callable 的 call 方法的執(zhí)行結(jié)果,但是要注意該方法會(huì)阻塞直到任務(wù)返回結(jié)果。zQH28資訊網(wǎng)——每日最新資訊28at.com

public interface Future<V> {    boolean cancel(boolean mayInterruptIfRunning);    boolean isCancelled();    boolean isDone();    V get() throws InterruptedException, ExecutionException;    V get(long timeout, TimeUnit unit)        throws InterruptedException, ExecutionException, TimeoutException;}

Java 的 J.U.C 里給出了 Future 接口的一個(gè)實(shí)現(xiàn) FutureTask,它同時(shí)實(shí)現(xiàn)了 Future 和 Runnable 接口,所以,F(xiàn)utureTask 既可以作為 Runnable 被線程執(zhí)行,又可以作為 Future 得到 Callable 的返回值。zQH28資訊網(wǎng)——每日最新資訊28at.com

下面是一個(gè) Callable 實(shí)現(xiàn)類和 FutureTask 結(jié)合使用讓主線程獲取子線程執(zhí)行結(jié)果的一個(gè)簡(jiǎn)單的示例:zQH28資訊網(wǎng)——每日最新資訊28at.com

package com.learnthread;import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;public class CallableDemo implements Callable<Integer> {    @Override    public Integer call() {        int i = 0;        for (i = 0; i < 20; i++) {            if (i == 5) {                break;            }            System.out.println(Thread.currentThread().getName() + " " + i);        }        return i;    }    public static void main(String[] args) {        CallableDemo tt = new CallableDemo();        FutureTask<Integer> ft = new FutureTask<>(tt);        Thread t = new Thread(ft);        t.start();        try {            System.out.println(Thread.currentThread().getName() + " " + ft.get());        } catch (Exception e) {            e.printStackTrace();        }    }}

上面我們把 FutureTask 作為 Thread 構(gòu)造方法的 Runnable 類型參數(shù) target 的實(shí)參,在它的基礎(chǔ)上創(chuàng)建線程, 執(zhí)行邏輯。所以本質(zhì)上 Callable + FutureTask 這種方式也是第二種通過實(shí)現(xiàn) Runnable 接口給線程指定執(zhí)行體的,只不過是由 FutureTask 包裝了一層,由它的 run 方法再去調(diào)用 Callable 的 call 方法。例程運(yùn)行后的輸出如下:zQH28資訊網(wǎng)——每日最新資訊28at.com

Thread-0 0Thread-0 1Thread-0 2Thread-0 3Thread-0 4main 5

Callable 更常用的方式是結(jié)合線程池來使用,在線程池接口 ExecutorService 中定義了多個(gè)可接收 Callable 作為線程執(zhí)行任務(wù)的方法 submit、invokeAny、invokeAll 等,這個(gè)等學(xué)到線程池了我們?cè)偃W(xué)習(xí)。zQH28資訊網(wǎng)——每日最新資訊28at.com

zQH28資訊網(wǎng)——每日最新資訊28at.com

Java 線程的使用陷阱

在剛開始接觸和學(xué)習(xí) Java 線程相關(guān)的知識(shí)時(shí),一個(gè)常見的錯(cuò)誤是,在創(chuàng)建線程的線程里,調(diào)用 Thread 對(duì)象的 run() 方法而不是調(diào)用 start() 方法。zQH28資訊網(wǎng)——每日最新資訊28at.com

Runnable myRunnable = new Runnable() {    @Override    public void run() {  System.out.println("Anonymous Runnable running");    }};Thread newThread = new Thread(myRunnable);newThread.run();  // 應(yīng)該調(diào)用 newThread.start();

起初你可能沒有注意到這么干有啥錯(cuò),因?yàn)?Runnable 的 run() 方法正常地被執(zhí)行,輸出了我們想要的結(jié)果。zQH28資訊網(wǎng)——每日最新資訊28at.com

但是,這么做 run() 不會(huì)由我們剛剛創(chuàng)建的新線程執(zhí)行,而是由創(chuàng)建 newThread 對(duì)象的線程執(zhí)行的 。要讓新創(chuàng)建的線程--newThread 調(diào)用 myRunnable 實(shí)例的 run() 方法,必須調(diào)用 newThread.start() 方法才行。zQH28資訊網(wǎng)——每日最新資訊28at.com

線程對(duì)象的基本用法

11.線程對(duì)象常用的方法

Thread 線程常用的方法有以下這些:zQH28資訊網(wǎng)——每日最新資訊28at.com

方法
zQH28資訊網(wǎng)——每日最新資訊28at.com

描述
zQH28資訊網(wǎng)——每日最新資訊28at.com

runzQH28資訊網(wǎng)——每日最新資訊28at.com

線程的執(zhí)行實(shí)體,不需要我們主動(dòng)調(diào)用,調(diào)用線程的start() 就會(huì)執(zhí)行run() 方法里的執(zhí)行體
zQH28資訊網(wǎng)——每日最新資訊28at.com

start
zQH28資訊網(wǎng)——每日最新資訊28at.com

線程的啟動(dòng)方法。
zQH28資訊網(wǎng)——每日最新資訊28at.com

Thread.currentThread
zQH28資訊網(wǎng)——每日最新資訊28at.com

Thread 類提供的靜態(tài)方法,返回對(duì)當(dāng)前正在執(zhí)行的線程對(duì)象的引用。
zQH28資訊網(wǎng)——每日最新資訊28at.com

setName
zQH28資訊網(wǎng)——每日最新資訊28at.com

設(shè)置線程名稱。
zQH28資訊網(wǎng)——每日最新資訊28at.com

getName
zQH28資訊網(wǎng)——每日最新資訊28at.com

獲取線程名稱。
zQH28資訊網(wǎng)——每日最新資訊28at.com

setPriority
zQH28資訊網(wǎng)——每日最新資訊28at.com

設(shè)置線程優(yōu)先級(jí)。Java 中的線程優(yōu)先級(jí)的范圍是 [1,10],一般來說,高優(yōu)先級(jí)的線程在運(yùn)行時(shí)會(huì)具有優(yōu)先權(quán)。可以通過 thread.setPriority(Thread.MAX_PRIORITY) 的方式設(shè)置,默認(rèn)優(yōu)先級(jí)為 5。
zQH28資訊網(wǎng)——每日最新資訊28at.com

getPriority
zQH28資訊網(wǎng)——每日最新資訊28at.com

獲取線程優(yōu)先級(jí)。
zQH28資訊網(wǎng)——每日最新資訊28at.com

setDaemon
zQH28資訊網(wǎng)——每日最新資訊28at.com

設(shè)置線程為守護(hù)線程。
zQH28資訊網(wǎng)——每日最新資訊28at.com

isDaemon
zQH28資訊網(wǎng)——每日最新資訊28at.com

判斷線程是否為守護(hù)線程。
zQH28資訊網(wǎng)——每日最新資訊28at.com

isAlive
zQH28資訊網(wǎng)——每日最新資訊28at.com

判斷線程是否啟動(dòng)。
zQH28資訊網(wǎng)——每日最新資訊28at.com

interrupt
zQH28資訊網(wǎng)——每日最新資訊28at.com

中斷線程的運(yùn)行。
zQH28資訊網(wǎng)——每日最新資訊28at.com

Thread.interrupted
zQH28資訊網(wǎng)——每日最新資訊28at.com

測(cè)試當(dāng)前線程是否已被中斷。
zQH28資訊網(wǎng)——每日最新資訊28at.com

join
zQH28資訊網(wǎng)——每日最新資訊28at.com

可以使一個(gè)線程強(qiáng)制運(yùn)行,線程強(qiáng)制運(yùn)行期間,其他線程無法運(yùn)行,必須等待此線程完成之后才可以繼續(xù)執(zhí)行。
zQH28資訊網(wǎng)——每日最新資訊28at.com

Thread.sleep
zQH28資訊網(wǎng)——每日最新資訊28at.com

靜態(tài)方法。將當(dāng)前正在執(zhí)行的線程休眠。
zQH28資訊網(wǎng)——每日最新資訊28at.com

Thread.yield
zQH28資訊網(wǎng)——每日最新資訊28at.com

靜態(tài)方法。將當(dāng)前正在執(zhí)行的線程暫停,讓出CPU,讓其他線程執(zhí)行。
zQH28資訊網(wǎng)——每日最新資訊28at.com

2.線程休眠

使用 Thread.sleep 方法可以使得當(dāng)前正在執(zhí)行的線程進(jìn)入休眠狀態(tài)。 使用 Thread.sleep 需要向其傳入一個(gè)整數(shù)值,這個(gè)值表示線程將要休眠的毫秒數(shù)。 Thread.sleep 方法可能會(huì)拋出 InterruptedException,因?yàn)楫惓2荒芸缇€程傳播回主線程中,因此必須在本地進(jìn)行處理。線程中拋出的其它異常也同樣需要在本地進(jìn)行處理。zQH28資訊網(wǎng)——每日最新資訊28at.com

public class ThreadSleepDemo {    public static void main(String[] args) {        new Thread(new MyThread("線程A", 500)).start();        new Thread(new MyThread("線程B", 1000)).start();        new Thread(new MyThread("線程C", 1500)).start();    }    static class MyThread implements Runnable {        /** 線程名稱 */        private String name;        /** 休眠時(shí)間 */        private int time;        private MyThread(String name, int time) {            this.name = name;            this.time = time;        }        @Override        public void run() {            try {                // 休眠指定的時(shí)間                Thread.sleep(this.time);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(this.name + "休眠" + this.time + "毫秒。");        }    }}

上面例程開啟了3個(gè)線程,在各自的線程執(zhí)行體里讓各自線程休眠了 500、1000 和 1500 ms ,線程 C 休眠結(jié)束后,整個(gè)程序退出。zQH28資訊網(wǎng)——每日最新資訊28at.com

線程A休眠500毫秒。線程B休眠1000毫秒。線程C休眠1500毫秒。Process finished with exit code 0

3.終止線程

當(dāng)一個(gè)線程運(yùn)行時(shí),另一個(gè)線程可以直接通過 interrupt 方法中斷其運(yùn)行狀態(tài)。zQH28資訊網(wǎng)——每日最新資訊28at.com

public class ThreadInterruptDemo {    public static void main(String[] args) {        MyThread mt = new MyThread(); // 實(shí)例化Runnable實(shí)現(xiàn)類的對(duì)象        Thread t = new Thread(mt, "線程"); // 實(shí)例化Thread對(duì)象        t.start(); // 啟動(dòng)線程        try {            Thread.sleep(2000); // 主線程休眠2秒        } catch (InterruptedException e) {            System.out.println("主線程休眠被終止");        }        t.interrupt(); // 中斷 mt 線程的執(zhí)行    }    static class MyThread implements Runnable {        @Override        public void run() {            System.out.println("1、進(jìn)入run()方法");            try {                Thread.sleep(10000); // 線程休眠10秒                System.out.println("2、已經(jīng)完成了休眠");            } catch (InterruptedException e) {                System.out.println("3、MyThread線程休眠被終止");                return; // 返回調(diào)用處            }            System.out.println("4、run()方法正常結(jié)束");        }    }}

如果一個(gè)線程的 run 方法執(zhí)行一個(gè)無限循環(huán),并且沒有執(zhí)行 sleep 等會(huì)拋出 InterruptedException 的操作,那么調(diào)用線程的 interrupt 方法就無法使線程提前結(jié)束。zQH28資訊網(wǎng)——每日最新資訊28at.com

不過調(diào)用 interrupt 方法會(huì)設(shè)置線程的中斷標(biāo)記,此時(shí)被設(shè)置中斷標(biāo)記的線程再調(diào)用 interrupted 方法會(huì)返回 true。因此可以在線程的執(zhí)行體循環(huán)體中使用 interrupted 方法來判斷當(dāng)前線程是否處于中斷狀態(tài),從而提前結(jié)束線程。zQH28資訊網(wǎng)——每日最新資訊28at.com

看下面這個(gè),可以有效終止線程執(zhí)行的示例:zQH28資訊網(wǎng)——每日最新資訊28at.com

package com.learnthread;import java.util.concurrent.TimeUnit;public class ThreadInterruptEffectivelyDemo {    public static void main(String[] args) throws Exception {        MyTask task = new MyTask();        Thread thread = new Thread(task, "線程-A");        thread.start();        TimeUnit.MILLISECONDS.sleep(50);        thread.interrupt();    }    private static class MyTask implements Runnable {        private volatile long count = 0L;        @Override        public void run() {            System.out.println(Thread.currentThread().getName() + " 線程啟動(dòng)");            // 通過 Thread.interrupted 和 interrupt 配合來控制線程終止            while (!Thread.interrupted()) {                System.out.println(count++);            }            System.out.println(Thread.currentThread().getName() + " 線程終止");        }    }}

主線程在啟動(dòng)線程-A后,主動(dòng)休眠50毫秒,線程-A的執(zhí)行體里會(huì)不斷打印計(jì)數(shù)器的值,等休眠結(jié)束后主線程通過調(diào)用線程-A的 interrupt 方法設(shè)置了線程的中斷標(biāo)記,這時(shí)線程-A的執(zhí)行體中通過 Thread.interrupted() 就能判斷出線程被設(shè)置了中斷狀態(tài),隨后結(jié)束執(zhí)行退出。zQH28資訊網(wǎng)——每日最新資訊28at.com

4.守護(hù)線程

(1) 什么是守護(hù)線程?zQH28資訊網(wǎng)——每日最新資訊28at.com

  • 守護(hù)線程(Daemon Thread)是在后臺(tái)執(zhí)行并且不會(huì)阻止 JVM 終止的線程。當(dāng)所有非守護(hù)線程結(jié)束時(shí),程序也就終止,同時(shí)會(huì)殺死所有守護(hù)線程。
  • 與守護(hù)線程(Daemon Thread)相反的,叫用戶線程(User Thread),也就是非守護(hù)線程。

(2) 為什么需要守護(hù)線程?zQH28資訊網(wǎng)——每日最新資訊28at.com

守護(hù)線程的優(yōu)先級(jí)比較低,用于為系統(tǒng)中的其它對(duì)象和線程提供服務(wù)。典型的應(yīng)用就是垃圾回收器。zQH28資訊網(wǎng)——每日最新資訊28at.com

(3) 如何使用守護(hù)線程?zQH28資訊網(wǎng)——每日最新資訊28at.com

可以使用 isDaemon 方法判斷線程是否為守護(hù)線程。zQH28資訊網(wǎng)——每日最新資訊28at.com

可以使用 setDaemon 方法設(shè)置線程為守護(hù)線程。zQH28資訊網(wǎng)——每日最新資訊28at.com

  • 正在運(yùn)行的用戶線程無法設(shè)置為守護(hù)線程,所以 setDaemon 必須在 thread.start 方法之前設(shè)置,否則會(huì)拋出 llegalThreadStateException 異常;
  • 一個(gè)守護(hù)線程創(chuàng)建的子線程依然是守護(hù)線程。
  • 不要認(rèn)為所有的應(yīng)用都可以分配給守護(hù)線程來進(jìn)行服務(wù),比如讀寫操作或者計(jì)算邏輯就不能。
public class ThreadDaemonDemo {    public static void main(String[] args) {        Thread t = new Thread(new MyThread(), "線程");        t.setDaemon(true); // 此線程在后臺(tái)運(yùn)行        System.out.println("線程 t 是否是守護(hù)進(jìn)程:" + t.isDaemon());        t.start(); // 啟動(dòng)線程    }    static class MyThread implements Runnable {        @Override        public void run() {            while (true) {                System.out.println(Thread.currentThread().getName() + "在運(yùn)行。");            }        }    }}

Java 線程的生命周期

java.lang.Thread.State 中定義了 6 種不同的線程狀態(tài),在給定的一個(gè)時(shí)刻,線程只能處于其中的一個(gè)狀態(tài)。 下圖給出了這六種狀態(tài),注意中間的 Ready 和 Running 都屬于 Runnable 就緒狀態(tài)。zQH28資訊網(wǎng)——每日最新資訊28at.com

zQH28資訊網(wǎng)——每日最新資訊28at.com

以下是各狀態(tài)的說明,以及狀態(tài)間的聯(lián)系:zQH28資訊網(wǎng)——每日最新資訊28at.com

  • 新建(New) - 尚未調(diào)用 start 方法的線程處于此狀態(tài)。此狀態(tài)意味著:線程創(chuàng)建了但尚未啟動(dòng)。
  • 就緒(Runnable) - 已經(jīng)調(diào)用了 start 方法的線程處于此狀態(tài)。此狀態(tài)意味著:線程已經(jīng)在 JVM 中運(yùn)行。但是在操作系統(tǒng)層面,它可能處于運(yùn)行狀態(tài),也可能等待資源調(diào)度(例如處理器資源),資源調(diào)度完成就進(jìn)入運(yùn)行狀態(tài)。所以該狀態(tài)的可運(yùn)行是指可以被運(yùn)行,具體有沒有運(yùn)行要看底層操作系統(tǒng)的資源調(diào)度。
  • 阻塞(Blocked) - 此狀態(tài)意味著:線程處于被阻塞狀態(tài)。表示線程在等待 synchronized 的隱式鎖(Monitor lock)。被 synchronized 修飾的方法、代碼塊同一時(shí)刻只允許一個(gè)線程執(zhí)行,其他線程只能等待,即處于阻塞狀態(tài)。當(dāng)占用 synchronized 隱式鎖的線程釋放鎖,并且等待的線程獲得 synchronized 隱式鎖后,就又會(huì)從 BLOCKED 轉(zhuǎn)換到 RUNNABLE 狀態(tài)。
  • 等待(Waiting) - 此狀態(tài)意味著:線程無限期等待,直到被其他線程顯式地喚醒。 阻塞和等待的區(qū)別在于,阻塞是被動(dòng)的,它是在等待獲取 synchronized 的隱式鎖。而等待是主動(dòng)的,線程通過調(diào)用 Object.wait 等方法進(jìn)入。
  • 定時(shí)等待(Timed waiting) - 此狀態(tài)意味著:無需等待其它線程顯式地喚醒,在一定時(shí)間之后會(huì)被系統(tǒng)自動(dòng)喚醒,線程通過調(diào)用設(shè)置了 Timeout 參數(shù)的Object.wait 等方法進(jìn)入。
  • 終止(Terminated) - 線程執(zhí)行完 run 方法,或者因異常退出了 run 方法。此狀態(tài)意味著:線程結(jié)束了生命周期。

下面這張圖更生動(dòng)地展示了線程狀態(tài)切換的時(shí)機(jī)和觸發(fā)條件(圖片來自網(wǎng)絡(luò),出處下方飲用鏈接1)。zQH28資訊網(wǎng)——每日最新資訊28at.com

zQH28資訊網(wǎng)——每日最新資訊28at.com

引用鏈接:zQH28資訊網(wǎng)——每日最新資訊28at.com

  • https://dunwu.github.io/javacore/concurrent/Java%E7%BA%BF%E7%A8%8B%E5%9F%BA%E7%A1%80.html
  • https://www.cnblogs.com/pcheng/p/6905340.html

本文鏈接:http://www.www897cc.com/showinfo-26-51228-0.html老后端被借調(diào)去寫Java了,含淚總結(jié)的Java多線程編程基礎(chǔ)

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: PHP老矣,尚能飯否?

下一篇: 如何快速分析軟件運(yùn)行瓶頸在哪里?推薦Linux下的一個(gè)強(qiáng)大命令工具

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 主站蜘蛛池模板: 平邑县| 通道| 鸡西市| 上林县| 阿拉善盟| 潍坊市| 安义县| 通化市| 南通市| 城固县| 清远市| 葫芦岛市| 水城县| 常山县| 贞丰县| 保定市| 汕头市| 东乡| 鹤壁市| 西乡县| 宕昌县| 玉屏| 扎赉特旗| 中阳县| 涟水县| 无锡市| 铜鼓县| 竹北市| 博乐市| 富川| 包头市| 白玉县| 修水县| 开封市| 昌黎县| 大化| 凤城市| 察隅县| 大埔县| 湟源县| 楚雄市|