国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Java中保證線(xiàn)程順序執行的操作代碼

發(fā)布時(shí)間:2021-07-05 18:40 來(lái)源:腳本之家 閱讀:0 作者:六層樓 欄目: 開(kāi)發(fā)技術(shù)

只要了解過(guò)多線(xiàn)程,我們就知道線(xiàn)程開(kāi)始的順序跟執行的順序是不一樣的。如果只是創(chuàng )建三個(gè)線(xiàn)程然后執行,最后的執行順序是不可預期的。這是因為在創(chuàng )建完線(xiàn)程之后,線(xiàn)程執行的開(kāi)始時(shí)間取決于CPU何時(shí)分配時(shí)間片,線(xiàn)程可以看成是相對于的主線(xiàn)程的一個(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();
    }
}

輸出結果:ACB/ABC/CBA...

那么我們該如何保證線(xiàn)程的順序執行呢?

如何保證線(xiàn)程的順序執行?

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

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

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();
    }
}

輸出結果:ABC

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

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

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();
    }
}

輸出結果:ABC

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

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

public class TicketExample2 {

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

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

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //信號量變更
                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();
    }
}

執行結果:
1 0
1 1
1 2
2 0
2 1
2 2
3 0
3 1
3 2

4. 使用Lock和信號量實(shí)現

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

public class TicketExample3 {
    //信號量
    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();
            //因為線(xiàn)程的執行順序是不可預期的,因此需要每個(gè)線(xiàn)程自旋
            System.out.println("線(xiàn)程" + name + " 開(kāi)始執行");
            if(ticket.get() != name) {
                try {
                    System.out.println("當前標識位為" + ticket.get() + ",線(xiàn)程" + name + " 開(kāi)始等待");
                    //開(kāi)始等待被喚醒
                    conditions[name - 1].await();
                    System.out.println("線(xiàn)程" + name + " 被喚醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(name);
            ticket.getAndIncrement();
            if (ticket.get() > 3) {
                ticket.set(1);
            }
            //執行完畢,喚醒下一次。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();
    }
}

輸出結果:
線(xiàn)程2 開(kāi)始執行
當前標識位為1,線(xiàn)程2 開(kāi)始等待
線(xiàn)程1 開(kāi)始執行
1
線(xiàn)程3 開(kāi)始執行
當前標識位為2,線(xiàn)程3 開(kāi)始等待
線(xiàn)程2 被喚醒
2
線(xiàn)程3 被喚醒
3

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

參考文章




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

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

日韩加勒比一本无码精品| 天堂网2017天堂网| 成人H无码动漫在线观看| 国产chinasex对白videos麻豆| 欧美性猛交XXXX富婆| 亚洲最大AV资源网在线观看|