最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中保證線程順序執(zhí)行的操作代碼

 更新時(shí)間:2021年05月15日 09:21:00   作者:六層樓  
本文給大家分享一篇教程關(guān)于java線程順序執(zhí)行問(wèn)題,如何保證線程的順序執(zhí)行呢?今天通過(guò)實(shí)例代碼給大家詳細(xì)講解下,感興趣的朋友跟隨小編一起看看吧

只要了解過(guò)多線程,我們就知道線程開(kāi)始的順序跟執(zhí)行的順序是不一樣的。如果只是創(chuàng)建三個(gè)線程然后執(zhí)行,最后的執(zhí)行順序是不可預(yù)期的。這是因?yàn)樵趧?chuàng)建完線程之后,線程執(zhí)行的開(kāi)始時(shí)間取決于CPU何時(shí)分配時(shí)間片,線程可以看成是相對(duì)于的主線程的一個(gè)異步操作。

public class FIFOThreadExample {
    public synchronized static void foo(String name) {
        System.out.print(name);
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> foo("A"));
        Thread thread2 = new Thread(() -> foo("B"));
        Thread thread3 = new Thread(() -> foo("C"));
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

輸出結(jié)果:ACB/ABC/CBA...

那么我們?cè)撊绾伪WC線程的順序執(zhí)行呢?

如何保證線程的順序執(zhí)行?

1. 使用Thread.join()實(shí)現(xiàn)

Thread.join()的作用是讓父線程等待子線程結(jié)束之后才能繼續(xù)運(yùn)行。以上述例子為例,main()方法所在的線程是父線程,在其中我們創(chuàng)建了3個(gè)子線程A,B,C,子線程的執(zhí)行相對(duì)父線程是異步的,不能保證順序性。而對(duì)子線程使用Thread.join()方法之后就可以讓父線程等待子線程運(yùn)行結(jié)束后,再開(kāi)始執(zhí)行父線程,這樣子線程執(zhí)行被強(qiáng)行變成了同步的,我們用Thread.join()方法就能保證線程執(zhí)行的順序性。

public class FIFOThreadExample {
    
    public static void foo(String name) {
        System.out.print(name);
    }

    public static void main(String[] args) throws InterruptedException{
        Thread thread1 = new Thread(() -> foo("A"));
        Thread thread2 = new Thread(() -> foo("B"));
        Thread thread3 = new Thread(() -> foo("C"));
        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();
        thread3.start();
    }
}

輸出結(jié)果:ABC

2. 使用單線程線程池來(lái)實(shí)現(xiàn)

另一種保證線程順序執(zhí)行的方法是使用一個(gè)單線程的線程池,這種線程池中只有一個(gè)線程,相應(yīng)的,內(nèi)部的線程會(huì)按加入的順序來(lái)執(zhí)行。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FIFOThreadExample {

    public static void foo(String name) {
        System.out.print(name);
    }

    public static void main(String[] args) throws InterruptedException{
        Thread thread1 = new Thread(() -> foo("A"));
        Thread thread2 = new Thread(() -> foo("B"));
        Thread thread3 = new Thread(() -> foo("C"));
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(thread1);
        executor.submit(thread2);
        executor.submit(thread3);
        executor.shutdown();
    }
}

輸出結(jié)果:ABC

3. 使用volatile關(guān)鍵字修飾的信號(hào)量實(shí)現(xiàn)

上面兩種的思路都是讓保證線程的執(zhí)行順序,讓線程按一定的順序執(zhí)行。這里介紹第三種思路,那就是線程可以無(wú)序運(yùn)行,但是執(zhí)行結(jié)果按順序執(zhí)行。
你應(yīng)該可以想到,三個(gè)線程都被創(chuàng)建并start(),這時(shí)候三個(gè)線程隨時(shí)都可能執(zhí)行run()方法。因此為了保證run()執(zhí)行的順序性,我們肯定需要一個(gè)信號(hào)量來(lái)讓線程知道在任意時(shí)刻能不能執(zhí)行邏輯代碼。
另外,因?yàn)槿齻€(gè)線程是獨(dú)立的,這個(gè)信號(hào)量的變化肯定需要對(duì)其他線程透明,因此volatile關(guān)鍵字也是必須要的。

public class TicketExample2 {

    //信號(hào)量
    static volatile int ticket = 1;
    //線程休眠時(shí)間
    public final static int SLEEP_TIME = 1;

    public static void foo(int name){
        //因?yàn)榫€程的執(zhí)行順序是不可預(yù)期的,因此需要每個(gè)線程自旋
        while (true) {
            if (ticket == name) {
                try {
                    Thread.sleep(SLEEP_TIME);
                    //每個(gè)線程循環(huán)打印3次
                    for (int i = 0; i < 3; i++) {
                        System.out.println(name + " " + i);
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //信號(hào)量變更
                ticket = name%3+1;
                return;

            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> foo(1));
        Thread thread2 = new Thread(() -> foo(2));
        Thread thread3 = new Thread(() -> foo(3));
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

執(zhí)行結(jié)果:
1 0
1 1
1 2
2 0
2 1
2 2
3 0
3 1
3 2

4. 使用Lock和信號(hào)量實(shí)現(xiàn)

此種方法的思想跟第三種方法是一樣的,都是不考慮線程執(zhí)行的順序而是考慮用一些方法控制線程執(zhí)行業(yè)務(wù)邏輯的順序。這里我們同樣用一個(gè)原子類型信號(hào)量ticket,當(dāng)然你可以不用原子類型,這里我只是為了保證自增操作的線程安全。然后我們用了一個(gè)可重入鎖ReentrantLock。用來(lái)給方法加鎖,當(dāng)一個(gè)線程拿到鎖并且標(biāo)識(shí)位正確的時(shí)候開(kāi)始執(zhí)行業(yè)務(wù)邏輯,執(zhí)行完畢后喚醒下一個(gè)線程。
這里我們不需要使用while進(jìn)行自旋操作了,因?yàn)長(zhǎng)ock可以讓我們喚醒指定的線程,所以改成if就可以實(shí)現(xiàn)順序的執(zhí)行。

public class TicketExample3 {
    //信號(hào)量
    AtomicInteger ticket = new AtomicInteger(1);
    public Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private Condition[] conditions = {condition1, condition2, condition3};

    public void foo(int name) {
        try {
            lock.lock();
            //因?yàn)榫€程的執(zhí)行順序是不可預(yù)期的,因此需要每個(gè)線程自旋
            System.out.println("線程" + name + " 開(kāi)始執(zhí)行");
            if(ticket.get() != name) {
                try {
                    System.out.println("當(dāng)前標(biāo)識(shí)位為" + ticket.get() + ",線程" + name + " 開(kāi)始等待");
                    //開(kāi)始等待被喚醒
                    conditions[name - 1].await();
                    System.out.println("線程" + name + " 被喚醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(name);
            ticket.getAndIncrement();
            if (ticket.get() > 3) {
                ticket.set(1);
            }
            //執(zhí)行完畢,喚醒下一次。1喚醒2,2喚醒3
            conditions[name % 3].signal();
        } finally {
            //一定要釋放鎖
            lock.unlock();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        TicketExample3 example = new TicketExample3();
        Thread t1 = new Thread(() -> {
            example.foo(1);
        });
        Thread t2 = new Thread(() -> {
            example.foo(2);
        });
        Thread t3 = new Thread(() -> {
            example.foo(3);
        });
        t1.start();
        t2.start();
        t3.start();
    }
}

輸出結(jié)果:
線程2 開(kāi)始執(zhí)行
當(dāng)前標(biāo)識(shí)位為1,線程2 開(kāi)始等待
線程1 開(kāi)始執(zhí)行
1
線程3 開(kāi)始執(zhí)行
當(dāng)前標(biāo)識(shí)位為2,線程3 開(kāi)始等待
線程2 被喚醒
2
線程3 被喚醒
3

上述的執(zhí)行結(jié)果并非唯一,但可以保證打印的順序一定是123這樣的順序。

參考文章

java 多線程 實(shí)現(xiàn)多個(gè)線程的順序執(zhí)行 - Hoonick - 博客園 (cnblogs.com)
Java lock鎖的一些細(xì)節(jié)_筆記小屋-CSDN博客
VolatileCallSite (Java Platform SE 8 ) (oracle.com)
java保證多線程的執(zhí)行順序 - james.yj - 博客園 (cnblogs.com)

以上就是Java中保證線程順序執(zhí)行的詳細(xì)內(nèi)容,更多關(guān)于java線程執(zhí)行順序的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 解決IDEA使用maven創(chuàng)建Web項(xiàng)目,出現(xiàn)500錯(cuò)誤的問(wèn)題

    解決IDEA使用maven創(chuàng)建Web項(xiàng)目,出現(xiàn)500錯(cuò)誤的問(wèn)題

    本文主要介紹了在使用Maven創(chuàng)建項(xiàng)目并導(dǎo)入依賴寫(xiě)完測(cè)試代碼后運(yùn)行出現(xiàn)500錯(cuò)誤的解決步驟,這種問(wèn)題的根本原因是Tomcat啟動(dòng)后缺少某些支持的jar包,導(dǎo)致運(yùn)行出錯(cuò),解決方法是在項(xiàng)目結(jié)構(gòu)中找到Artifacts,點(diǎn)擊要編輯的項(xiàng)目
    2024-10-10
  • JAVA集成本地部署的DeepSeek的圖文教程

    JAVA集成本地部署的DeepSeek的圖文教程

    本文主要介紹了JAVA集成本地部署的DeepSeek的圖文教程,包含配置環(huán)境變量及下載DeepSeek-R1模型并啟動(dòng),具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-03-03
  • Java?Optional用法面試題精講

    Java?Optional用法面試題精講

    這篇文章主要為大家介紹了Java?Optional用法面試題精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • spring boot集成pagehelper(兩種方式)

    spring boot集成pagehelper(兩種方式)

    這篇文章主要介紹了spring boot集成pagehelper(兩種方式),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • 使用Springboot打成jar包thymeleaf的問(wèn)題

    使用Springboot打成jar包thymeleaf的問(wèn)題

    這篇文章主要介紹了使用Springboot打成jar包thymeleaf的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java中的ReentrantReadWriteLock實(shí)現(xiàn)原理詳解

    Java中的ReentrantReadWriteLock實(shí)現(xiàn)原理詳解

    這篇文章主要介紹了Java中的ReentrantReadWriteLock實(shí)現(xiàn)原理詳解,讀寫(xiě)鎖實(shí)現(xiàn)了接口ReadWriteLock,適合于讀多寫(xiě)少的情況,支持公平鎖和非公平鎖,支持可沖入(進(jìn)入讀鎖后可再進(jìn)入讀鎖,進(jìn)入寫(xiě)鎖后可再進(jìn)入寫(xiě)鎖和讀鎖),需要的朋友可以參考下
    2024-01-01
  • java書(shū)店系統(tǒng)畢業(yè)設(shè)計(jì) 用戶模塊(2)

    java書(shū)店系統(tǒng)畢業(yè)設(shè)計(jì) 用戶模塊(2)

    這篇文章主要介紹了java書(shū)店系統(tǒng)畢業(yè)設(shè)計(jì),第二步系統(tǒng)總體設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • 圖片驗(yàn)證碼概述及實(shí)現(xiàn)步驟

    圖片驗(yàn)證碼概述及實(shí)現(xiàn)步驟

    本文主要介紹了圖片驗(yàn)證碼概述及實(shí)現(xiàn)步驟。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • java實(shí)現(xiàn)圖片上傳至本地實(shí)例詳解

    java實(shí)現(xiàn)圖片上傳至本地實(shí)例詳解

    我們給大家分享了關(guān)于java實(shí)現(xiàn)圖片上傳至本地的實(shí)例以及相關(guān)代碼,有需要的朋友參考下。
    2018-08-08
  • JAVA中的FileWriter流解析

    JAVA中的FileWriter流解析

    這篇文章主要介紹了JAVA中的FileWriter流解析,FileWriter類提供了多種寫(xiě)入字符的方法,包括寫(xiě)入單個(gè)字符、寫(xiě)入字符數(shù)組和寫(xiě)入字符串等,它還提供了一些其他的方法,如刷新緩沖區(qū)、關(guān)閉文件等,需要的朋友可以參考下
    2023-10-10

最新評(píng)論

调兵山市| 淮安市| 托克逊县| 黔南| 怀集县| 涿鹿县| 原平市| 宝清县| 莲花县| 永州市| 邵东县| 自治县| 禄劝| 伊金霍洛旗| 沙河市| 阿拉尔市| 福海县| 当涂县| 延川县| 邵阳县| 同江市| 高雄县| 北川| 剑河县| 定襄县| 得荣县| 红原县| 宣城市| 常山县| 札达县| 濮阳县| 吐鲁番市| 景泰县| 宿迁市| 乡城县| 廉江市| 河北区| 浠水县| 绥德县| 准格尔旗| 酉阳|