欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java多線程Thread類的使用及注意事項

 更新時間:2022年06月20日 11:27:50   作者:sugar?high  
這篇文章主要介紹了Java多線程Thread類的使用及注意事項,在java標準庫中提供了一個Thread類來表示/操作線程,Thread類也可以視為是java標準庫提供的API

Thread類的基本用法

創(chuàng)建子類,繼承自Thread并且重寫run方法:

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("hello thread");
    }
}
public class Demo1 {
    public static void main(String[] args) {
        // 最基本的創(chuàng)建線程的辦法.
        Thread t = new MyThread();
        //調(diào)用了start方法才是真正的在系統(tǒng)中創(chuàng)建了線程,執(zhí)行run方法
        t.start();
    }
}

創(chuàng)建一個類,實現(xiàn)Runnable接口再創(chuàng)建Runnable是實例傳給Thread

class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("hello");
    }
}
public class Demo3 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
    }
}

匿名內(nèi)部類:

創(chuàng)建了一個匿名內(nèi)部類,繼承自Thread類,同時重寫run方法,再new出匿名內(nèi)部類的實例

public class Demo4 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                System.out.println("hello");
            }
        };
        t.start();
    }
}

new的Runnable,針對這個創(chuàng)建的匿名內(nèi)部類,同時new出的Runnable實例傳給Thread的構(gòu)造方法

public class Demo5 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
        t.start();
    }
}

lambda表達式 lambda代替Runnable

public class Demo6 {
    public static void main(String[] args) {
        Thread t = new Thread(() ->{
            System.out.println("hello");
        });
        t.start();
    }
}

線程指標

  • 1.isDaemon();是否后臺線程 后臺線程不影響進程退出,不是后臺線程會影響進程退出
  • 2.isAlive();是否存活 在調(diào)用start前系統(tǒng)中是沒有對應線程的,run方法執(zhí)行完后線程就銷毀了,t對象可能還存在
  • 3.isinterrupted();是否被中斷

run和start的區(qū)別:run單純的只是一個普通方法描述了任務的內(nèi)容 start則是一個特殊的方法,內(nèi)部會在系統(tǒng)中創(chuàng)建線程

中斷線程

線程停下來的關(guān)鍵是要讓對應run方法執(zhí)行完,對于main線程來說main方法執(zhí)行完了才會終止

1.手動設置標志位

在線程中控制這個標志位就能影響到這個線程結(jié)束,但是此處多個線程共用一片虛擬空間,因此main線程修改的isQuit和t線程判斷的isQuit是同一個值

public class Demo10 {
    // 通過這個變量來控制線程是否結(jié)束.
    private static boolean isQuit = false;
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (!isQuit) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();

        // 就可以在 main 線程中通過修改 isQuit 的值, 來影響到線程是否退出
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // main 線程在 5s 之后, 修改 isQuit 的狀態(tài).
        isQuit = true;
    }
}

2.使用Thread中內(nèi)置的一個標志位來判定

Thread.interruted()這是一個靜態(tài)方法 Thread.currentThread().isInterrupted()這是一個實例方法,其中currentThread能夠獲取到當前線程的實例

public class Demo7 {
    public static void main(String[] args)  {
        Thread t = new Thread(() -> {
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 當觸發(fā)異常之后, 立即就退出循環(huán)~
                    System.out.println("這是收尾工作");
                    break;
                }
            }
        });
        t.start();
        try{
            Thread.sleep(5000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        // 在主線程中, 調(diào)用 interrupt 方法, 來中斷這個線程.
        // t.interrupt 的意思就是讓 t 線程被中斷!!
        t.interrupt();
    }
}

需要注意的是調(diào)用這個方法t.interrupt()可能會產(chǎn)生兩種情況:

  • 1)如果t線程處在就緒就設置線程的標志位為true
  • 2)如果t線程處在阻塞狀態(tài)(sleep),就會觸發(fā)一個InterruptExeception

線程等待

多個線程之間調(diào)度順序是不確定的,有時候我們需要控制線程之間的順序,線程等待就是一種控制線程執(zhí)行順序的手段,此處的線程等待只要是控制線程結(jié)束的先后順序。
哪個線程中的join,哪個線程就會阻塞等待直到對應的線程執(zhí)行完畢為止。

  • t.join();調(diào)用這個方法的線程是main線程,針對t這個對象調(diào)用的此時就是讓main等待t。代碼執(zhí)行到join這一行就停下了,讓t先結(jié)束然后main繼續(xù)。
  • t.join(10000);join提供了另一個版本為帶一個參數(shù)的,參數(shù)為等待時間10s之后join直接返回不再等待

Thread.currentThread()

能夠獲取當前線程的應用,哪個線程調(diào)用的currentThread就獲取到哪個線程的實例 對比this如下:

對于這個代碼來說,通過繼承Thread的方法來創(chuàng)建線程。此時run方法中直接通過this拿到的就是當前Thread的實例

public class Demo4 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
                System.out.println(this.getName());
            }
        };
        t.start();
    }
}

然而此處this不是指向Thread類型,而是指向Runnable,Runnable只是一個單純的任務沒有name屬性,要想拿到線程名字只能通過Thread.currentThread()

public class Demo5 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                //err
                //System.out.println(this.getName());
                //right
                System.out.println(Thread.currentThread().getName());
            }
        });
        t.start();
    }
}

進程狀態(tài)

針對系統(tǒng)層面:

  • 就緒
  • 阻塞

java中Thread類進一步細化:

  • NEW:把Thread對象創(chuàng)建好了但是還沒有調(diào)用start
  • TERMINATED:操作系統(tǒng)中的線程已執(zhí)行完畢銷毀,但是Thread對象還在獲取到的狀態(tài)
  • RUNNABLE:就緒狀態(tài),處在該狀態(tài)的線程就是在就緒隊列中,隨時可以調(diào)度到CPU上
  • TIME_WAITING:調(diào)用了sleep就會進入到該狀態(tài),join(超時時間) BLOCKED:當前線程在等待鎖導致了阻塞
  • WAITING:當前線程在等待喚醒

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

線程安全問題

定義:操作系統(tǒng)中線程調(diào)度是隨機的,導致程序的執(zhí)行可能會出現(xiàn)一些bug。如果因為調(diào)度隨機性引入了bug線程就是不安全的,反之則是安全的。
解決方法:加鎖,給方法直接加上synchronized關(guān)鍵字,此時進入方法就會自動加鎖,離開方法就會自動解鎖。當一個線程加鎖成功的時候,其他線程嘗試加鎖就會觸發(fā)阻塞等待,阻塞會一直持續(xù)到占用鎖的線程把鎖釋放為止。

synchronized public void increase() {
        count++;
}

線程不安全產(chǎn)生的原因:

  • 1.線程是搶占式執(zhí)行,線程間的調(diào)度充滿隨機性。
  • 2.多個線程對同一個變量進行修改操作
  • 3.針對變量的操作不是原子的
  • 4.內(nèi)存可見性也會影響線程安全(針對同一個變量t1線程循環(huán)進行多次讀操作,t2線程少次修改操作,t1就不會從內(nèi)存讀數(shù)據(jù)了而是從寄存器里讀)
  • 5.指令重排序,也是編譯器優(yōu)化的一種操作,保證邏輯不變的情況下調(diào)整順序,解決方法synchronized。

內(nèi)存可見性解決方法:

  • 1.使用synchronized關(guān)鍵字 使用synchronized不光能保證指令的原子性,同時也能保證內(nèi)存的可見性。被synchronized包裹起來的代碼編譯器就不會從寄存器里讀。
  • 2.使用volatile關(guān)鍵字 能夠保證內(nèi)存可見性,禁止編譯器作出上述優(yōu)化,編譯器每次執(zhí)行判定相等都會重新從內(nèi)存讀取。

synchronized用法

在java中每個類都是繼承自Object,每個new出來的實例里面一方面包含自己安排的屬性,另一方面包含了“對象頭”即對象的一些元數(shù)據(jù)。加鎖操作就是在這個對象頭里面設置一個標志位。

1.直接修飾普通的方法

使用synchronized的時候本質(zhì)上是對某個“對象”進行加鎖,此時的鎖對象就是this。加鎖操作就是在設置this的對象頭的標志位,當兩個線程同時嘗試對同一個對象加鎖的時候才有競爭,如果是兩個線程在針對兩個不同對象加鎖就沒有競爭。

class Counter{
	public int count;
	synchronized public void increase(){
	count++;
	}
}

2.修飾一個代碼塊

需要顯示制定針對那個對象加鎖(java中的任意對象都可以作為鎖對象)

public void increase(){
    synchronized(this){
    count++;
    }
}

3.修飾一個靜態(tài)方法

相當于針對當前類的類對象加鎖,類對象就是運行程序的時候。class文件被加載到JVM內(nèi)存中的模樣。

synchronized public static void func(){
}

或者

public static void func(){
    synchronized(Counter.class){

    }
}

監(jiān)視器鎖monitor lock

可重入鎖就是同一個線程針對同一個鎖,連續(xù)加鎖兩次,如果出現(xiàn)死鎖就是不可重入鎖,如果不會死鎖就是可重入的。因此就把synchronized實現(xiàn)為可重入鎖,下面的例子里啊連續(xù)加鎖操作不會導致死鎖??芍厝腈i內(nèi)部會記錄所被哪個線程占用也會記錄加鎖次數(shù),因此后續(xù)再加鎖就不是真的加鎖而是單純地把技術(shù)給自增。

synchronized public void increase(){
    synchronized(this){
        count++;
    }
}

死鎖的其他場景

  • 1.一個線程一把鎖
  • 2.兩個線程兩把鎖
  • 3.N個線程M把鎖(哲學家就餐問題,解決方法:先拿編號小的筷子)

死鎖的四個必要條件(前三個都是鎖本身的特點)

  • 1.互斥使用,一個鎖被另一個線程占用后其他線程就用不了(鎖的本質(zhì),保證原子性)
  • 2.不可搶占,一個鎖被一個線程占用后其他線程不可把這個鎖給挖走
  • 3.請求和保持,當一個線程占據(jù)了多把鎖之后,除非顯示的釋放否則這些鎖中都是該線程持有的
  • 4.環(huán)路等待,等待關(guān)系成環(huán)(解決:遵循固定的順序加鎖就不會出現(xiàn)環(huán)路等待)

java線程類:

  • 不安全的:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StringBuilder
  • 安全的:Vector,HashTable,ConcurrentHashMap,StringBuffer,String

volatile

禁止編譯器優(yōu)化保證內(nèi)存可見性,產(chǎn)生原因:計算機想執(zhí)行一些計算就需要把內(nèi)存的數(shù)據(jù)讀到CPU寄存器中,然后再從寄存器中計算寫回到內(nèi)存中,因為CPU訪問寄存器的速度比訪問內(nèi)存快很多,當CPU連續(xù)多次訪問內(nèi)存結(jié)果都一樣,CPU就會選擇訪問寄存器。

JMM(Java Memory Model)Java內(nèi)存模型

就是把硬件結(jié)構(gòu)在java中用專業(yè)的術(shù)語又重新抽象封裝了一遍。

  • 工作內(nèi)存(work memory)其實指的不是內(nèi)存,而是CPU寄存器。
  • 主內(nèi)存(main memeory)這才是主內(nèi)存。
  • 原因:java作為一個跨平臺編程語言要把硬件細節(jié)封裝起來,假設某個計算機沒有CPU或者內(nèi)存同樣可以套到上述模型中。

寄存器,緩存和內(nèi)存之間的關(guān)系

CPU從內(nèi)存取數(shù)據(jù)太慢,因此把數(shù)據(jù)直接放到寄存器里來讀,但寄存器空間太緊張于是又搞了一個存儲空間,比寄存器大比內(nèi)存小速度比寄存器慢比內(nèi)存快稱為緩存。寄存器和緩存統(tǒng)稱為工作內(nèi)存。

寄存器,緩存和內(nèi)存之間的關(guān)系圖

  • 存儲空間:CPU<L1<L2<L3<內(nèi)存
  • 速度:CPU>L1>L2>L3>內(nèi)存
  • 成本:CPU>L1>L2>L3>內(nèi)存

volatile和synchronized的區(qū)別

  • volatile只是保證可見性不保證原子性,只是處理一個線程讀和一個線程寫的過程。
  • synchronized都能處理

wait和notify

等待和通知處理線程調(diào)度隨機性問題的,join也是一種控制順序的方式更傾向于控制線程結(jié)束。wait和notify都是Object對象的方法,調(diào)用wait方法的線程就會陷入阻塞,阻塞到有線程通過notify來通知。

public class Demo9 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        System.out.println("wait前");
        object.wait();
        System.out.println("wait后");
    }
}

wait內(nèi)部會做三件事;

  • 1.先釋放鎖
  • 2.等待其他線程的通知
  • 3.收到通知后重新獲得鎖并繼續(xù)往下執(zhí)行

因此想用wait/notify就得搭配synchronized

public class Demo9 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        synchronized (object){
            System.out.println("wait前");
            object.wait();
            System.out.println("wait后");
        }
    }
}

注意:wait notify都是針對同一對象來操作的,例如現(xiàn)在有一個對象o,有10個線程都調(diào)用了o.wait,此時10個線程都是阻塞狀態(tài)。如果調(diào)用了o.notify就會把10個線程中的一個線程喚醒。而notifyAll就會把所有10個線程全都給喚醒,此時就會競爭鎖。

到此這篇關(guān)于Java多線程Thread類的使用及注意事項的文章就介紹到這了,更多相關(guān)Java Thread 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于mybatis注解動態(tài)sql中foreach工具的方法

    基于mybatis注解動態(tài)sql中foreach工具的方法

    這篇文章主要介紹了mybatis注解動態(tài)sql中foreach工具方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java實現(xiàn)獲取圖片和視頻文件的Exif信息

    Java實現(xiàn)獲取圖片和視頻文件的Exif信息

    這篇文章將重點為大家介紹一下如何使用Java編程語言結(jié)合metadata-extractor去自動獲取全景圖片的Exif信息,獲取照片的拍攝坐標信息,感興趣的可以了解一下
    2022-11-11
  • Jenkins自動化打包為war包

    Jenkins自動化打包為war包

    這篇文章主要介紹了Jenkins自動化打包為war包,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • 基于HttpClient上傳文件中文名亂碼的解決

    基于HttpClient上傳文件中文名亂碼的解決

    這篇文章主要介紹了HttpClient上傳文件中文名亂碼的解決方案,具有很好的參考價值,希望對大家有所幫助。
    2021-07-07
  • 搭建maven私有倉庫的方法實現(xiàn)

    搭建maven私有倉庫的方法實現(xiàn)

    Maven是一個流行的Java項目管理工具,它可以幫助我們管理項目的構(gòu)建、報告和文檔,本文主要介紹了搭建maven私有倉庫的方法實現(xiàn),感興趣的可以了解一下
    2023-05-05
  • Java使用JMeter進行高并發(fā)測試

    Java使用JMeter進行高并發(fā)測試

    軟件的壓力測試是一種保證軟件質(zhì)量的行為,本文主要介紹了Java使用JMeter進行高并發(fā)測試,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • SpringBoot中實現(xiàn)@Scheduled動態(tài)定時任務

    SpringBoot中實現(xiàn)@Scheduled動態(tài)定時任務

    SpringBoot中的@Scheduled注解為定時任務提供了一種很簡單的實現(xiàn),本文主要介紹了SpringBoot中實現(xiàn)@Scheduled動態(tài)定時任務,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • Java圖像處理之獲取用戶感興趣的區(qū)域

    Java圖像處理之獲取用戶感興趣的區(qū)域

    OpenCV(Open?Source?Computer?Vision?Library)是一個開源的計算機視覺庫,它提供了很多函數(shù),這些函數(shù)非常高效地實現(xiàn)了計算機視覺算法。本文將利用OpenCV實現(xiàn)獲取用戶感興趣的區(qū)域,從而達到摳圖效果
    2022-07-07
  • Java的線程阻塞、中斷及優(yōu)雅退出方法詳解

    Java的線程阻塞、中斷及優(yōu)雅退出方法詳解

    這篇文章主要介紹了Java的線程阻塞、中斷及優(yōu)雅退出方法詳解,Java中的線程阻塞是指當一個線程無法繼續(xù)執(zhí)行時,它會進入阻塞狀態(tài),直到某個條件滿足后才能繼續(xù)執(zhí)行,線程阻塞可以通過多種方式實現(xiàn),需要的朋友可以參考下
    2023-10-10
  • 使用maven插件對java工程進行打包過程解析

    使用maven插件對java工程進行打包過程解析

    這篇文章主要介紹了使用maven插件對java工程進行打包過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-08-08

最新評論