亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

實(shí)際應(yīng)用中,多線程非常有用,例如,QQ音樂就是一個多線程程序,我們可以一邊聽音樂,一般下載音樂,還可以同時播放MV等非常方便。一個Web服務(wù)器通過多線程同時處理多個請求,比如Tomcat就是多線程的。
前言

前段時間推出的JAVA8新特性文章收到大家廣泛關(guān)注和好評,非常感謝各位支持,這段時間苦思冥想,決定輸出一波Java多線程技能點(diǎn),希望可以在大家的工作和面試中有所幫助!本篇文章為多線程系列第一章,主要講解一下幾點(diǎn):

多線程好處和應(yīng)用場景

多線程的相關(guān)概念和術(shù)語

Java線程創(chuàng)建方式

Thread類詳解,線程的常用方法

線程5種狀態(tài)和6種狀態(tài),兩種版本解釋

線程狀態(tài)之間轉(zhuǎn)換

Java設(shè)計(jì)者寫過一個很有影響力的白皮書,用來解釋設(shè)計(jì)的初衷,并發(fā)布了一個簡短的摘要,分為11個術(shù)語:

  • 簡單性
  • 面向?qū)ο?/li>
  • 分布式
  • 健壯性
  • 安全性
  • 體系結(jié)構(gòu)中立
  • 可移植性
  • 解釋型
  • 高性能
  • 多線程
  • 動態(tài)性

其中多線程就是本次要接觸的,白皮書中對多線程的解釋:

多線程可以帶來更好的交互響應(yīng)和實(shí)時行為。

如今,我們非常關(guān)注并發(fā)性,因?yàn)槟柖尚袑⑼杲Y(jié)。我們不再追求更快的處理器,而是著眼于獲得更多的處理器,而且要讓它們一直保持工作。不過,可以看到,大多數(shù)編程語言對于這個問題并沒有顯示出足夠的重視。Java在當(dāng)時很超前。它是第一個支持并發(fā)程序設(shè)計(jì)的主流語言。從白皮書中可以看到,它的出發(fā)點(diǎn)稍有些不同。當(dāng)時,多核處理器還很神秘,而Web編程才剛剛起步,處理器要花很長時間等待服務(wù)器響應(yīng),需要并發(fā)程序設(shè)計(jì)來確保用戶界面不會"凍住"。并發(fā)程序設(shè)計(jì)絕非易事,不過Java在這方面表現(xiàn)很出色,可以很好地管理這個工作。

在操作系統(tǒng)中有多任務(wù)【multitasking】,在同一刻運(yùn)行多個程序【應(yīng)用】的能力。例如,在聽音樂的同時可以邊打游戲,邊寫代碼。如今我們的電腦大多都是多核CPU,但是,并發(fā)執(zhí)行的進(jìn)程【正在執(zhí)行的應(yīng)用】數(shù)目并不是由CPU數(shù)目制約的。操作系統(tǒng)將CPU的時間片分配給每一個進(jìn)程,給人并行處理的感覺。

相關(guān)概念

程序【program】:為了完成特定任務(wù),用某種語言編寫的一組指令的集合。程序就是一堆代碼,一組數(shù)據(jù)和指令集,是一個靜態(tài)的概念。就說我們程序員寫的那玩意。比如:安裝在電腦或者手機(jī)上的各種軟件,今日頭條、抖音、懂車帝等,如果一個程序支持多線程,這個程序就是一個多線程程序

進(jìn)程【Process】:是程序的一次執(zhí)行過程或者說是正在運(yùn)行的程序,是一個動態(tài)概念,進(jìn)程存在生命周期,也就是說程序隨著程序的終止而銷毀

線程【Thread】:線程是進(jìn)程中的實(shí)際運(yùn)作的單位,是進(jìn)程的一條流水線,是程序的實(shí)際執(zhí)行者,是最小的執(zhí)行單位。通常在一個進(jìn)程中可以包含若干個線程,當(dāng)然一個進(jìn)程中至少有一個線程。線程是CPU調(diào)度和執(zhí)行的最小單位

CPU時間片:時間片即CPU分配給各個程序的時間,每個進(jìn)程被分配一個時間段,稱作它的時間片,即該進(jìn)程允許運(yùn)行的時間,使各個程序從表面上看是同時進(jìn)行的,如果在時間片結(jié)束時進(jìn)程還在運(yùn)行,則CPU將被剝奪并分配給另一個進(jìn)程。如果進(jìn)程在時間片結(jié)束前阻塞或結(jié)束,則CPU當(dāng)即進(jìn)行切換。而不會造成CPU資源浪費(fèi)

并行【parallel】:多個任務(wù)同時進(jìn)行,并行必須有多核才能實(shí)現(xiàn),否則只能是并發(fā),比如:多名學(xué)生有問題,同時有多名老師可以輔導(dǎo)解決

串行【serial】:一個程序處理完當(dāng)前進(jìn)程,按照順序接著處理下一個進(jìn)程,一個接著一個進(jìn)行,比如:多名學(xué)生有問題,只有一名老師,需要挨個解決

并發(fā)【concurrency】:同一個對象被多個線程同時操作。(這是一種假并行。即一個CPU的情況下,在同一個時間點(diǎn),CPU只能執(zhí)行一個代碼,因?yàn)榍袚Q的很快,所以就有同時執(zhí)行的錯覺),比如:多名學(xué)生有問題,只有一個老師,他一會處理A同學(xué),一會處理B同學(xué),一會處理C同學(xué),頻繁切換,看起來好似在同時處理學(xué)生問題

多線程意義

實(shí)際應(yīng)用中,多線程非常有用,例如,QQ音樂就是一個多線程程序,我們可以一邊聽音樂,一般下載音樂,還可以同時播放MV等非常方便。一個Web服務(wù)器通過多線程同時處理多個請求,比如Tomcat就是多線程的。

注意:程序會因?yàn)橐攵嗑€程而變的復(fù)雜,多線程同時會帶來一些問題,需要我們解決

多線程應(yīng)用場景

  • 程序需要同時執(zhí)行兩個或多個任務(wù)。
  • 程序需要實(shí)現(xiàn)一些需要等待的任務(wù)時,如用戶輸入、文件讀寫操作、網(wǎng)絡(luò)操作、搜索等。
  • 需要一些后臺運(yùn)行的程序時。

多線程多數(shù)在瀏覽器、Web服務(wù)器、數(shù)據(jù)庫、各種專用服務(wù)器【如游戲服務(wù)器】、分布式計(jì)算等場景出現(xiàn)。

在使用Java編寫后臺服務(wù)時,如果遇到并發(fā)較高、需要后臺任務(wù)、需要長時間處理大數(shù)據(jù)等情況都可以創(chuàng)建線程單獨(dú)的線程處理這些事項(xiàng),多線程的目的就在于提高處理速度,減少用戶等待時間

  • 后臺記錄日志,創(chuàng)建額外線程來記錄日志
  • 大量用戶請求,創(chuàng)建多個線程共同處理請求
  • 下載大文件,可以創(chuàng)建單獨(dú)的線程,不影響正常的業(yè)務(wù)流暢度
  • ......

多線程創(chuàng)建方式

線程創(chuàng)建有4種方式:

方式1:繼承Thread類

方式2:實(shí)現(xiàn)Runnable接口

方式3:實(shí)現(xiàn)Callable接口

方式4:使用線程池【這塊后邊單獨(dú)說,它更像是管理線程的手段】

繼承Thread

步驟:

  • 自定義類繼承Thread類
  • 重寫run方法,run方法就是線程的功能執(zhí)行體
  • 創(chuàng)建線程對象,調(diào)用start方法啟動線程
  • 啟動線程之后,線程不一定會立即執(zhí)行,需要得到CPU分配的時間片,也就是拿到CPU執(zhí)行權(quán)限才會執(zhí)行

JDK源碼中,Thread類定義實(shí)現(xiàn)了Runnable接口

所以知道重寫的run方法從哪來的了吧!就是從Runnable接口中來的

需求:創(chuàng)建線程計(jì)算10以內(nèi)的偶數(shù)

線程類:

 
public class ThreadTest extends Thread{

    // run方法是 線程體,啟動線程時會運(yùn)行run()方法中的代碼
    @Override
    public void run() {
        // 輸出10以內(nèi)偶數(shù)
        for (int i = 0; i < 10; i++) {
            if (i % 2 == 0){
                System.out.println(i);
            }
        }
    }
}

測試類:

測試類中輸出了一句話:主線程

 
public class ThreadMain {
    public static void main(String[] args) {
        // 1、創(chuàng)建線程對象
        ThreadTest t1 = new ThreadTest();
        // 2、調(diào)用start方法啟動線程
        t1.start();
        System.out.println("主線程");
    }
}

打印結(jié)果:

實(shí)現(xiàn)Runnable接口

步驟:

  • 自定義類實(shí)現(xiàn)Runnable接口
  • 實(shí)現(xiàn)run方法
  • 創(chuàng)建實(shí)現(xiàn)類對象
  • 創(chuàng)建Thread對象,在構(gòu)造方法中傳入實(shí)現(xiàn)類對象作為參數(shù)
  • 調(diào)用Thread對象的start方法啟動線程

同樣的需求打印10以內(nèi)的偶數(shù)

實(shí)現(xiàn)類:

 
public class RunnableImpl implements Runnable{

    @Override
    public void run() {
        // 輸出10以內(nèi)偶數(shù)
        for (int i = 0; i < 10; i++) {
            if (i % 2 == 0){
                System.out.println(i);
            }
        }
    }
}

測試類:

 
public class RunnableMain {
    public static void main(String[] args) {
        // 1、創(chuàng)建實(shí)現(xiàn)類對象
        RunnableImpl runnable = new RunnableImpl();
        // 2、創(chuàng)建線程對象,接收實(shí)現(xiàn)類,因?yàn)閷?shí)現(xiàn)類中的run方法承載了線程的功能
        Thread t1 = new Thread(runnable);
        // 3、啟動線程
        t1.start();
        // 主線程
        System.out.println("主線程");
    }
}

Callable接口

FutureTask類:

RunnableFuture接口:

步驟:

  • 新建類實(shí)現(xiàn)Callable接口,并指定泛型類型,類型就是線程計(jì)算之后的結(jié)果的類型
  • 實(shí)現(xiàn)call方法,call方法跟run方法類似,不過該方法有返回值和異常拋出,都是來封裝線程功能體的
  • 在測試類中創(chuàng)建實(shí)現(xiàn)類對象,并且創(chuàng)建 FutureTask 對象將實(shí)現(xiàn)類對象當(dāng)做構(gòu)造方法參數(shù)傳入
  • 創(chuàng)建Thread線程對象,將 FutureTask 對象當(dāng)做構(gòu)造方法參數(shù)傳入,并調(diào)用start方法啟動線程
  • 可以通過 FutureTask 對象的get方法獲取線程的運(yùn)算結(jié)果

案例:還是計(jì)算10以內(nèi)的偶數(shù),這一次將計(jì)算結(jié)果返回,因?yàn)橛卸鄠€數(shù)據(jù)所以返回?cái)?shù)據(jù)用集合存儲,則Callable接口的泛型類型應(yīng)該是集合

實(shí)現(xiàn)類:

 
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
// 1、實(shí)現(xiàn)Callable,指明泛型類型
public class CallableImpl implements Callable<List<Integer>> {

    // 2、線程返回Integer類型數(shù)據(jù),拋出異常
    @Override
    public List<Integer> call() throws Exception {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            if (i % 2 == 0){
                // 3、偶數(shù)存儲到集合中
                list.add(i);
            }
        }
        // 4、返回集合
        return list;
    }
}

測試類:

 
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableMain {
    public static void main(String[] args) {

        // 1、創(chuàng)建Callable實(shí)現(xiàn)類對象
        CallableImpl callable = new CallableImpl();
        // 2、創(chuàng)建 FutureTask對象傳入 callable
        // FutureTask 實(shí)現(xiàn) 了 RunnableFuture,RunnableFuture實(shí)現(xiàn)了Runnable接口和Future接口
        FutureTask<List<Integer>> task = new FutureTask<>(callable);
        // 3、將 task 傳入線程對象
        Thread t1 = new Thread(task);
        // 4、啟動線程
        t1.start();
        // 5、獲取線程返回?cái)?shù)據(jù)
        try {
            List<Integer> list = task.get();
            System.out.println(list);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

三種實(shí)現(xiàn)方式區(qū)別

  • Java單繼承的特性決定,使用實(shí)現(xiàn)接口的方式創(chuàng)建線程更靈活
  • Callable實(shí)現(xiàn)call方法有返回值和異常拋出,方便定位問題,配合FutureTask可以獲取線程運(yùn)算結(jié)果,而run方法沒有返回值,沒有異常拋出
  • 如果線程運(yùn)行結(jié)束后不需要返回值,則推薦使用實(shí)現(xiàn)Runnable接口方式

小貼士:有不少文章中寫到【實(shí)現(xiàn)的方式更適合用來處理多個線程有共享數(shù)據(jù)的情況】,很多小伙伴也拿去背,這句話怎么看都不對吧,多線程共享數(shù)據(jù)不加鎖,不同步怎么著也不能避免線程安全問題!

線程開辟

  • 開辟線程需要通過Thread類創(chuàng)建對象
  • 啟動線程需要Thread對象調(diào)用start方法
  • 線程的功能可以裝在Thread類的run方法或者Runnable接口實(shí)現(xiàn)類的run方法類中
  • 線程開辟需要在另一個線程中開啟,新開辟的線程稱為子線程

小貼士:對于Java應(yīng)用程序java.exe來講,至少會存在三個線程:

main主線程

gc垃圾回收線程

異常處理線程,如果發(fā)生異常會影響主線程。

線程狀態(tài)

線程的狀態(tài)網(wǎng)上有 5種狀態(tài) 和 6種狀態(tài) 兩個版本

五種狀態(tài)版本:是基于現(xiàn)代操作系統(tǒng)線程狀態(tài)角度解釋的

新建:當(dāng)一個Thread類或其子類的對象被聲明并創(chuàng)建時,新生的線程對象處于新建狀態(tài)

就緒:處于新建狀態(tài)的線程被start后,將進(jìn)入線程隊(duì)列等待CPU時間片,此時它已具備了運(yùn)行的條件,只是沒分配到CPU資源

運(yùn)行:當(dāng)就緒的線程被調(diào)度并獲得CPU資源時,便進(jìn)入運(yùn)行狀態(tài),run方法定義了線程的操作和功能

阻塞:在某種特殊情況下,被人為掛起或執(zhí)行輸入輸出操作時,讓出CPU并臨時終止自己的執(zhí)行,進(jìn)入阻塞狀態(tài)

死亡:線程完成了它的全部工作或線程被提前強(qiáng)制性地中止或出現(xiàn)異常導(dǎo)致結(jié)束

在JDK5的時候Thread類中定義了一個State枚舉類,其中定義了6種線程狀態(tài),這是Java官方定義的Java線程的6種狀態(tài)

1)NEW:處于NEW狀態(tài)的線程此時尚未啟動。只是通過new Thread()創(chuàng)建了線程對象,并未調(diào)用start()方法

2)RUNNABLE:Java線程的 RUNNABLE 狀態(tài)其實(shí)是包括了傳統(tǒng)操作系統(tǒng)線程的 就緒(ready) 和 運(yùn)行(running) 兩個狀態(tài)的。處于 RUNNABLE 狀態(tài)的線程可能在 Java 虛擬機(jī)中運(yùn)行,也有可能在等待 CPU 分配資源

3)BLOCKED:阻塞狀態(tài)。處于 BLOCKED 狀態(tài)的線程正等待鎖的釋放以進(jìn)入同步區(qū),就好比你去食堂打飯,只有一個窗口你就得排隊(duì),等前邊的人結(jié)束之后你完成打飯

4)WAITING :等待狀態(tài)。處于等待狀態(tài)的線程變成 RUNNABLE 狀態(tài)需要其他線程喚醒

可以通過調(diào)用一下三個方法進(jìn)入等待狀態(tài):

  • Object.wait():使當(dāng)前線程處于等待狀態(tài)直到另一個線程喚醒它;
  • Thread.join():使當(dāng)前線程等待另一個線程執(zhí)行完畢之后再繼續(xù)執(zhí)行,底層調(diào)用的是 Object 實(shí)例的 wait() 方法;
  • LockSupport.park():除非獲得調(diào)用許可,否則禁用當(dāng)前線程進(jìn)行線程調(diào)度

5)TIMED_WAITING:超時等待狀態(tài)。線程等待一個具體的時間,時間到后會被自動喚醒。

調(diào)用如下方法會使線程進(jìn)入超時等待狀態(tài):

  • Thread.sleep(long millis):使當(dāng)前線程睡眠指定時間,sleep() 方法不會釋放當(dāng)前鎖,但會讓出 CPU,所以其他不需要爭奪鎖的線程可以獲取 CPU 執(zhí)行;
  • 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)前線程進(jìn)行線程調(diào)度指定時間;
  • LockSupport.parkUntil(long deadline):同上,也是禁止線程進(jìn)行調(diào)度指定時間;

6)TERMINATED:終止?fàn)顟B(tài)。此時線程已執(zhí)行完畢。

其實(shí)等待和鎖定狀態(tài)可以被籠統(tǒng)的稱為阻塞狀態(tài),就是停著不動了嘛,在回答面試題時建議回答6種狀態(tài)版本,就是是JDK源碼中定義的,一來有官方支持,二來證明咱看過一點(diǎn)源碼。

狀態(tài)轉(zhuǎn)換

  • 新建狀態(tài)的線程調(diào)用start方法進(jìn)入到運(yùn)行狀態(tài)
  • 運(yùn)行狀態(tài)線程如果遇到Object.wait()、Thread.join()或者LockSupport.park()方法則會放棄CPU執(zhí)行權(quán)進(jìn)入等待狀態(tài),這個裝需要被喚醒之后才會再次進(jìn)入就緒狀態(tài)獲得到CPU時間片進(jìn)入運(yùn)行狀態(tài)
  • 運(yùn)行狀態(tài)的線程遇到Thread.sleep(long)、Object.wait(long)、Thread.join(long)等方法,也就是可以傳入時間的,就會進(jìn)入超時等待狀態(tài),達(dá)到時間之后就會自動進(jìn)入就緒狀態(tài),當(dāng)CPU執(zhí)行就進(jìn)入運(yùn)行狀態(tài)
  • 運(yùn)行狀態(tài)的線程如果被同步代碼塊或者同步方法包裹,執(zhí)行時如果釋放鎖資源,就會進(jìn)入阻塞狀態(tài)或者叫鎖定狀態(tài),只有再次獲取到鎖資源時才會進(jìn)入就緒狀態(tài),等到CPU時間片后進(jìn)入運(yùn)行狀態(tài)
  • 執(zhí)行完的線程就會進(jìn)入終止?fàn)顟B(tài),線程結(jié)束

線程之間的狀態(tài)轉(zhuǎn)換可以參考下圖

Thread類詳解

成員變量

變量名

類型

作用

name

volatile String

線程名稱

priority

int

線程的優(yōu)先級,默認(rèn)為5,范圍1-10

threadQ

Thread

 

eetop

long

 

single_step

boolean

是否單步執(zhí)行

daemon

boolean

守護(hù)線程狀態(tài),默認(rèn)為false

stillborn

boolean

JVM狀態(tài),默認(rèn)為false

target

target

將被執(zhí)行的Runnable實(shí)現(xiàn)類

group

ThreadGroup

當(dāng)前線程的線程組

contextClassLoader

ClassLoader

這個線程上下文的類加載器

inheritedAccessControlContext

AccessControlContext

該線程繼承的AccessControlContext

threadInitNumber

static int

用于匿名線程的自動編號

threadLocals

ThreadLocal.ThreadLocalMap

屬于此線程的ThreadLocal,這個映射關(guān)系通過ThreadLocal維持

inheritableThreadLocals

ThreadLocal.ThreadLocalMap

這個線程的InheritableThreadLocal,其映射關(guān)系通過InheritableThreadLocal維持

stackSize

long

此線程的請求的堆棧的大小,如果創(chuàng)建者的請求堆棧大小為0,則不指定堆棧大小,由jvm來自行決定。一些jvm會忽略這個參數(shù)。

nativeParkEventPointer

long

在本機(jī)線程終止后持續(xù)存在的jvm私有狀態(tài)。

tid

long

線程的ID

threadSeqNumber

static long

用于生成線程的ID

threadStatus

volatile int

java線程狀態(tài),0表示未啟動

parkBlocker

volatile Object

提供給LockSupport調(diào)用的參數(shù)

blocker

volatile Interruptible

此線程在可中斷的IO操作中被阻塞的對象,阻塞程序的中斷方法應(yīng)該在設(shè)置了這個線程中斷狀態(tài)之后被調(diào)用

常量

 
/**
 * The minimum priority that a thread can have.
 */
public final static int MIN_PRIORITY = 1;

/**
 * The default priority that is assigned to a thread.
 */
public final static int NORM_PRIORITY = 5;

/**
 * The maximum priority that a thread can have.
 */
public final static int MAX_PRIORITY = 10;

常量名

數(shù)據(jù)類型

作用

MIN_PRIORITY

int

線程最低優(yōu)先級

NORM_PRIORITY

int

分配給線程的默認(rèn)優(yōu)先級

MAX_PRIORITY

int

線程最大優(yōu)先級

Thread構(gòu)造方法

從源碼看出Thread類一共有9個構(gòu)造方法,除第三個為default修飾【同包可用】,其他都是public

構(gòu)造方法

作用

Thread()

分配新的Thread對象

Thread(Runnable target)

傳入Runnable接口實(shí)現(xiàn)類,之后由JVM啟動線程

Thread(Runnable target, AccessControlContext acc)

在傳入Runnable的時候還可以指定AccessControlContext

Thread(ThreadGroup group, Runnable target)

指定線程組和Runnable接口

Thread(String name)

指定線程名字,默認(rèn)是【Thread-下一個線程編號,從0開始】

Thread(ThreadGroup group, String name)

指定線程組和線程名字

Thread(Runnable target, String name)

指定Runnable接口和線程名

Thread(ThreadGroup group, Runnable target, String name)

指定線程組,Runnable接口和線程名

Thread(ThreadGroup group, Runnable target, String name,long stackSize)

指定線程組,Runnable接口,線程名和此線程請求的堆棧大小,默認(rèn)為0

Thread常用方法

方法

返回值類型

作用

start()

void

啟動線程

run()

void

重寫的Runnable接口方法,封裝線程的功能體

currentThread()

Thread

靜態(tài)方法,獲取當(dāng)前線程

getName()

String

獲取線程名

setName(String name)

void

設(shè)置線程名

yield()

void

主動釋放當(dāng)前線程的執(zhí)行權(quán)

join()

void

在線程中插入執(zhí)行另一個線程,該線程被阻塞,直到插入執(zhí)行的線程完全執(zhí)行完畢以后,該線程才繼續(xù)執(zhí)行下去

sleep(long millis)

void

線程休眠一段時間

isAlive()

boolean

判斷線程是否還存活

isDaemon()

boolean

判斷是否為守護(hù)線程

stop()

void

過時方法。當(dāng)執(zhí)行此方法時,強(qiáng)制結(jié)束當(dāng)前線程,因過于粗暴,會引發(fā)很多問題所以棄用

setDaemon(boolean on)

void

設(shè)置為守護(hù)線程

getPriority()

int

獲取線程優(yōu)先級

setPriority(int newPriority)

void

設(shè)置線程優(yōu)先級

設(shè)置線程名

實(shí)現(xiàn)類:

 
public class RunnableImpl implements Runnable{

    @Override
    public void run() {
        // 輸出10以內(nèi)偶數(shù)
        for (int i = 0; i < 10; i++) {
            if (i % 2 == 0){
                // 獲取當(dāng)前線程
                Thread thread = Thread.currentThread();
                // 獲取線程名
                String threadName = thread.getName();
                System.out.println(threadName + "===>" + i);
            }
        }
    }
}

測試類:

 
public class RunnableMain {
    public static void main(String[] args) {
        // 1、創(chuàng)建實(shí)現(xiàn)類對象
        RunnableImpl runnable = new RunnableImpl();
        // 2、 創(chuàng)建線程對象,并指定線程名
        Thread t1 = new Thread(runnable, "線程1");
        // 3、啟動線程
        t1.start();

        System.out.println(Thread.currentThread().getName() + "主線程");
    }
}

運(yùn)行結(jié)果:

或者通過setName()方法設(shè)置線程名

 
public class RunnableMain {
    public static void main(String[] args) {
        // 1、創(chuàng)建實(shí)現(xiàn)類對象
        RunnableImpl runnable = new RunnableImpl();
        // 2、 創(chuàng)建線程對象,不指定名字
        Thread t1 = new Thread(runnable);
        // 設(shè)置線程名
        t1.setName("線程1");
        // 3、啟動線程
        t1.start();

        System.out.println(Thread.currentThread().getName() + "主線程");
    }
}

如果不設(shè)置線程名,默認(rèn)為【"Thread-" + nextThreadNum()】,nextThreadNum方法使用 threadInitNumber靜態(tài)變量,默認(rèn)從0開始,每次+1

不設(shè)置線程名運(yùn)行效果如下

sleep方法

sleep方法可以讓線程阻塞指定的毫秒數(shù)。時間到了后,線程進(jìn)入就緒狀態(tài)。sleep可用來研模擬網(wǎng)絡(luò)延時,倒計(jì)時等。每一個對象都有一個鎖,sleep不會釋放鎖,鎖的概念后邊會詳細(xì)講解

實(shí)現(xiàn)類:

 
public class RunnableImpl implements Runnable{

    @Override
    public void run() {
        // 輸出10以內(nèi)偶數(shù)
        for (int i = 0; i < 10; i++) {
            if (i % 2 == 0){
                try {
                    // 休眠1秒
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + "===>" + i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

測試類:

 
public class RunnableMain {
    public static void main(String[] args) {
        // 1、創(chuàng)建實(shí)現(xiàn)類對象
        RunnableImpl runnable = new RunnableImpl();
        // 2、 創(chuàng)建線程對象,不指定名字
        Thread t1 = new Thread(runnable,"線程1");
        // 3、啟動線程
        t1.start();
    }
}

運(yùn)行結(jié)果:

"善用"sleep年入百萬不是夢:

yield方法

提出申請釋放CPU資源,至于能否成功釋放取決于JVM決定,調(diào)用yield()方法后,線程仍然處于RUNNABLE狀態(tài),線程不會進(jìn)入阻塞狀態(tài),保留了隨時被調(diào)用的權(quán)利

實(shí)現(xiàn)類:

 
public class RunnableImpl implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "開始執(zhí)行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName() + "執(zhí)行結(jié)束");
    }
}

測試類:

 
public class RunnableMain {
    public static void main(String[] args) {
        // 1、創(chuàng)建實(shí)現(xiàn)類對象
        RunnableImpl runnable = new RunnableImpl();
        // 2、 創(chuàng)建線程對象,不指定名字
        Thread t1 = new Thread(runnable,"線程1");
        Thread t2 = new Thread(runnable,"線程2");
        // 3、啟動線程
        t1.start();
        t2.start();
    }
}

運(yùn)行結(jié)果:

第五次執(zhí)行是線程2執(zhí)行開始結(jié)束后輸出的線程1開始結(jié)束,這就說明CPU并沒有切換到別的線程,說明并沒有釋放CPU資源

join方法

將當(dāng)前的線程掛起,當(dāng)前線程阻塞,待其他的線程執(zhí)行完畢,當(dāng)前線程才能執(zhí)行,可以把join()方法理解為插隊(duì),誰插到前面,誰先執(zhí)行

在很多情況下,主線程創(chuàng)建并啟動子線程,如果子線程中要進(jìn)行大量的耗時運(yùn)算,主線程將可能早于子線程結(jié)束。如果主線程需要知道子線程的執(zhí)行結(jié)果時,就需要等待子線程執(zhí)行結(jié)束了。主線程可以sleep(xx),但這樣的xx時間不好確定,因?yàn)樽泳€程的執(zhí)行時間不確定,join()方法比較合適這個場景

 
public class RunnableMain {
    public static void main(String[] args) {
        // 1、lambda創(chuàng)建線程
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    // 模擬耗時操作
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + "join方法===>" + i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        // 2、 啟動線程
        t1.start();
        try {
            // t1調(diào)用join 方法
            t1.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("main線程");
    }
}

運(yùn)行結(jié)果:

設(shè)置優(yōu)先級

改變、獲取線程的優(yōu)先級。Java提供一個線程調(diào)度器來監(jiān)控程序中啟動后進(jìn)入就緒狀態(tài)的所有線程,線程調(diào)度器按照優(yōu)先級決定應(yīng)該調(diào)度哪個線程來執(zhí)行。線程的優(yōu)先級用數(shù)據(jù)表示,范圍1~10。線程的優(yōu)先級高只是表示他的權(quán)重大,獲取CPU執(zhí)行權(quán)的幾率大。先設(shè)置線程的優(yōu)先級,在執(zhí)行start()方法

 
public class RunnableMain {
    public static void main(String[] args) {
        // 1、lambda創(chuàng)建線程
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "線程優(yōu)先級" + Thread.currentThread().getPriority());
        },"線程1");

        Thread t2 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "線程優(yōu)先級" + Thread.currentThread().getPriority());
        },"線程2");
        // 2、設(shè)置線程優(yōu)先級
        t1.setPriority(1);
        t2.setPriority(10);
        // 3、 啟動線程
        t1.start();
        t2.start();

        System.out.println("main線程");
    }
}

結(jié)束線程

JDK提供的【stop()、destroy()】兩種方法已廢棄,不推薦再使用。推薦線程自動停止下來,就比如上邊的所有案例,都是執(zhí)行完了run方法中的所有代碼之后線程就自然結(jié)束了。如果線程需要循環(huán)執(zhí)行,建議使用一個標(biāo)識位變量進(jìn)行終止,當(dāng)flag=false時,則終止線程運(yùn)行

比如:定義一個名為【線程1】的子線程,當(dāng)主線程執(zhí)行3次循環(huán)之后,線程1停止運(yùn)行

實(shí)現(xiàn)類:

 
public class RunnableImpl implements Runnable{
    // boolean變量標(biāo)記是否需要繼續(xù)執(zhí)行
    private boolean flag = true;

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        // 循環(huán)執(zhí)行,flag為false時停止
        while (flag) {
            System.out.println(Thread.currentThread().getName() + "正在運(yùn)行");
        }
    }
}

測試類:

 
public class RunnableMain {
    public static void main(String[] args) {
        RunnableImpl runnable = new RunnableImpl();
        Thread t1 = new Thread(runnable, "線程1");
        t1.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主線程====》" + i);
            // 當(dāng)循環(huán)三次時
            if(i == 3) {
                // 設(shè)置flag值為false
                runnable.setFlag(false);
            }
        }
    }
}

總結(jié)

  • 掌握多線程的使用場景和術(shù)語
  • 熟練創(chuàng)建和啟動線程
  • 掌握線程狀態(tài)和狀態(tài)之間轉(zhuǎn)換
  • 掌握Thread類中的常用方法如:join、sleep、yield等

分享到:
標(biāo)簽:Java
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定