博客專(zhuān)欄

EEPW首頁(yè) > 博客 > 面試官:線(xiàn)程順序執(zhí)行,這么多答案你都答不上來(lái)?

面試官:線(xiàn)程順序執(zhí)行,這么多答案你都答不上來(lái)?

發(fā)布人:編碼之外 時(shí)間:2021-03-07 來(lái)源:工程師 發(fā)布文章
一.前言

本文使用了7中方法實(shí)現(xiàn)在多線(xiàn)程中讓線(xiàn)程按順序運(yùn)行的方法,涉及到多線(xiàn)程中許多常用的方法,不止為了知道如何讓線(xiàn)程按順序運(yùn)行,更是讓讀者對(duì)多線(xiàn)程的使用有更深刻的了解。使用的方法如下:

  • [1] 使用線(xiàn)程的join方法
  • [2] 使用主線(xiàn)程的join方法
  • [3] 使用線(xiàn)程的wait方法
  • [4] 使用線(xiàn)程的線(xiàn)程池方法
  • [5] 使用線(xiàn)程的Condition(條件變量)方法
  • [6] 使用線(xiàn)程的CountDownLatch(倒計(jì)數(shù))方法
  • [7] 使用線(xiàn)程的CyclicBarrier(回環(huán)柵欄)方法
  • [8] 使用線(xiàn)程的Semaphore(信號(hào)量)方法
二.實(shí)現(xiàn)

我們下面需要完成這樣一個(gè)應(yīng)用場(chǎng)景:

1.早上;2.測(cè)試人員、產(chǎn)品經(jīng)理、開(kāi)發(fā)人員陸續(xù)的來(lái)公司上班;3.產(chǎn)品經(jīng)理規(guī)劃新需求;4.開(kāi)發(fā)人員開(kāi)發(fā)新需求功能;5.測(cè)試人員測(cè)試新功能。

規(guī)劃需求,開(kāi)發(fā)需求新功能,測(cè)試新功能是一個(gè)有順序的,我們把thread1看做產(chǎn)品經(jīng)理,thread2看做開(kāi)發(fā)人員,thread3看做測(cè)試人員。

1.使用線(xiàn)程的join方法

join():是Theard的方法,作用是調(diào)用線(xiàn)程需等待該join()線(xiàn)程執(zhí)行完成后,才能繼續(xù)用下運(yùn)行。

應(yīng)用場(chǎng)景:當(dāng)一個(gè)線(xiàn)程必須等待另一個(gè)線(xiàn)程執(zhí)行完畢才能執(zhí)行時(shí)可以使用join方法。

package com.wwj.javabase.thread.order;

/**
 * @author wwj
 * 通過(guò)子程序join使線(xiàn)程按順序執(zhí)行
 */

public class ThreadJoinDemo {

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("產(chǎn)品經(jīng)理規(guī)劃新需求");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread1.join();
                    System.out.println("開(kāi)發(fā)人員開(kāi)發(fā)新需求功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.join();
                    System.out.println("測(cè)試人員測(cè)試新功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測(cè)試人員來(lái)上班了...");
        thread3.start();
        System.out.println("產(chǎn)品經(jīng)理來(lái)上班了...");
        thread1.start();
        System.out.println("開(kāi)發(fā)人員來(lái)上班了...");
        thread2.start();
    }
}

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

早上:
測(cè)試人員來(lái)上班了...
產(chǎn)品經(jīng)理來(lái)上班了...
開(kāi)發(fā)人員來(lái)上班了...
產(chǎn)品經(jīng)理規(guī)劃新需求
開(kāi)發(fā)人員開(kāi)發(fā)新需求功能
測(cè)試人員測(cè)試新功能
2.使用主線(xiàn)程的join方法

這里是在主線(xiàn)程中使用join()來(lái)實(shí)現(xiàn)對(duì)線(xiàn)程的阻塞。

package com.wwj.javabase.thread.order;

/**
 * @author wwj
 * 通過(guò)主程序join使線(xiàn)程按順序執(zhí)行
 */

public class ThreadMainJoinDemo {

    public static void main(String[] args) throws Exception {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("產(chǎn)品經(jīng)理正在規(guī)劃新需求...");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("開(kāi)發(fā)人員開(kāi)發(fā)新需求功能");
            }
        });

        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("測(cè)試人員測(cè)試新功能");
            }
        });

        System.out.println("早上:");
        System.out.println("產(chǎn)品經(jīng)理來(lái)上班了");
        System.out.println("測(cè)試人員來(lái)上班了");
        System.out.println("開(kāi)發(fā)人員來(lái)上班了");
        thread1.start();
        //在父進(jìn)程調(diào)用子進(jìn)程的join()方法后,父進(jìn)程需要等待子進(jìn)程運(yùn)行完再繼續(xù)運(yùn)行。
        System.out.println("開(kāi)發(fā)人員和測(cè)試人員休息會(huì)...");
        thread1.join();
        System.out.println("產(chǎn)品經(jīng)理新需求規(guī)劃完成!");
        thread2.start();
        System.out.println("測(cè)試人員休息會(huì)...");
        thread2.join();
        thread3.start();
    }
}

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

產(chǎn)品經(jīng)理來(lái)上班了
測(cè)試人員來(lái)上班了
開(kāi)發(fā)人員來(lái)上班了
開(kāi)發(fā)人員和測(cè)試人員休息會(huì)...
產(chǎn)品經(jīng)理正在規(guī)劃新需求...
產(chǎn)品經(jīng)理新需求規(guī)劃完成!
測(cè)試人員休息會(huì)...
開(kāi)發(fā)人員開(kāi)發(fā)新需求功能
測(cè)試人員測(cè)試新功能
3.使用線(xiàn)程的wait方法

wait():是Object的方法,作用是讓當(dāng)前線(xiàn)程進(jìn)入等待狀態(tài),同時(shí),wait()也會(huì)讓當(dāng)前線(xiàn)程釋放它所持有的鎖。“直到其他線(xiàn)程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法”,當(dāng)前線(xiàn)程被喚醒(進(jìn)入“就緒狀態(tài)”)

notify()和notifyAll():是Object的方法,作用則是喚醒當(dāng)前對(duì)象上的等待線(xiàn)程;notify()是喚醒單個(gè)線(xiàn)程,而notifyAll()是喚醒所有的線(xiàn)程。

wait(long timeout):讓當(dāng)前線(xiàn)程處于“等待(阻塞)狀態(tài)”,“直到其他線(xiàn)程調(diào)用此對(duì)象的notify()方法或 notifyAll() 方法,或者超過(guò)指定的時(shí)間量”,當(dāng)前線(xiàn)程被喚醒(進(jìn)入“就緒狀態(tài)”)。

應(yīng)用場(chǎng)景:Java實(shí)現(xiàn)生產(chǎn)者消費(fèi)者的方式。

package com.wwj.javabase.thread.order;

/**
 * @author wwj
 */

public class ThreadWaitDemo {

    private static Object myLock1 = new Object();
    private static Object myLock2 = new Object();

    /**
     * 為什么要加這兩個(gè)標(biāo)識(shí)狀態(tài)?
     * 如果沒(méi)有狀態(tài)標(biāo)識(shí),當(dāng)t1已經(jīng)運(yùn)行完了t2才運(yùn)行,t2在等待t1喚醒導(dǎo)致t2永遠(yuǎn)處于等待狀態(tài)
     */

    private static Boolean t1Run = false;
    private static Boolean t2Run = false;
    public static void main(String[] args) {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (myLock1){
                    System.out.println("產(chǎn)品經(jīng)理規(guī)劃新需求...");
                    t1Run = true;
                    myLock1.notify();
                }
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (myLock1){
                    try {
                        if(!t1Run){
                            System.out.println("開(kāi)發(fā)人員先休息會(huì)...");
                            myLock1.wait();
                        }
                        synchronized (myLock2){
                            System.out.println("開(kāi)發(fā)人員開(kāi)發(fā)新需求功能");
                            myLock2.notify();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (myLock2){
                    try {
                        if(!t2Run){
                            System.out.println("測(cè)試人員先休息會(huì)...");
                            myLock2.wait();
                        }
                        System.out.println("測(cè)試人員測(cè)試新功能");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測(cè)試人員來(lái)上班了...");
        thread3.start();
        System.out.println("產(chǎn)品經(jīng)理來(lái)上班了...");
        thread1.start();
        System.out.println("開(kāi)發(fā)人員來(lái)上班了...");
        thread2.start();
    }
}

運(yùn)行結(jié)果:這里輸出會(huì)有很多種順序,主要是因?yàn)榫€(xiàn)程進(jìn)入的順序,造成鎖住線(xiàn)程的順序不一致。

早上:
測(cè)試人員來(lái)上班了...
產(chǎn)品經(jīng)理來(lái)上班了...
開(kāi)發(fā)人員來(lái)上班了...
測(cè)試人員先休息會(huì)...
產(chǎn)品經(jīng)理規(guī)劃新需求...
開(kāi)發(fā)人員開(kāi)發(fā)新需求功能
測(cè)試人員測(cè)試新功能
4.使用線(xiàn)程的線(xiàn)程池方法

JAVA通過(guò)Executors提供了四種線(xiàn)程池

  • 單線(xiàn)程化線(xiàn)程池(newSingleThreadExecutor);
  • 可控最大并發(fā)數(shù)線(xiàn)程池(newFixedThreadPool);
  • 可回收緩存線(xiàn)程池(newCachedThreadPool);
  • 支持定時(shí)與周期性任務(wù)的線(xiàn)程池(newScheduledThreadPool)。

單線(xiàn)程化線(xiàn)程池(newSingleThreadExecutor):優(yōu)點(diǎn),串行執(zhí)行所有任務(wù)。

submit():提交任務(wù)。

shutdown():方法用來(lái)關(guān)閉線(xiàn)程池,拒絕新任務(wù)。

應(yīng)用場(chǎng)景:串行執(zhí)行所有任務(wù)。如果這個(gè)唯一的線(xiàn)程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線(xiàn)程來(lái)替代它。此線(xiàn)程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。

package com.wwj.javabase.thread.order;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author wwj
 * 通過(guò)SingleThreadExecutor讓線(xiàn)程按順序執(zhí)行
 */

public class ThreadPoolDemo {

    static ExecutorService executorService = Executors.newSingleThreadExecutor();

    public static void main(String[] args) throws Exception {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("產(chǎn)品經(jīng)理規(guī)劃新需求");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("開(kāi)發(fā)人員開(kāi)發(fā)新需求功能");
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("測(cè)試人員測(cè)試新功能");
            }
        });

        System.out.println("早上:");
        System.out.println("產(chǎn)品經(jīng)理來(lái)上班了");
        System.out.println("測(cè)試人員來(lái)上班了");
        System.out.println("開(kāi)發(fā)人員來(lái)上班了");
        System.out.println("領(lǐng)導(dǎo)吩咐:");
        System.out.println("首先,產(chǎn)品經(jīng)理規(guī)劃新需求...");
        executorService.submit(thread1);
        System.out.println("然后,開(kāi)發(fā)人員開(kāi)發(fā)新需求功能...");
        executorService.submit(thread2);
        System.out.println("最后,測(cè)試人員測(cè)試新功能...");
        executorService.submit(thread3);
        executorService.shutdown();
    }
}

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

早上:
產(chǎn)品經(jīng)理來(lái)上班了
測(cè)試人員來(lái)上班了
開(kāi)發(fā)人員來(lái)上班了
領(lǐng)導(dǎo)吩咐:
首先,產(chǎn)品經(jīng)理規(guī)劃新需求...
然后,開(kāi)發(fā)人員開(kāi)發(fā)新需求功能...
最后,測(cè)試人員測(cè)試新功能...
產(chǎn)品經(jīng)理規(guī)劃新需求
開(kāi)發(fā)人員開(kāi)發(fā)新需求功能
測(cè)試人員測(cè)試新功能
5.使用線(xiàn)程的Condition(條件變量)方法

Condition(條件變量):通常與一個(gè)鎖關(guān)聯(lián)。需要在多個(gè)Contidion中共享一個(gè)鎖時(shí),可以傳遞一個(gè)Lock/RLock實(shí)例給構(gòu)造方法,否則它將自己生成一個(gè)RLock實(shí)例。

  • Condition中**await()**方法類(lèi)似于Object類(lèi)中的wait()方法。
  • Condition中**await(long time,TimeUnit unit)**方法類(lèi)似于Object類(lèi)中的wait(long time)方法。
  • Condition中**signal()**方法類(lèi)似于Object類(lèi)中的notify()方法。
  • Condition中**signalAll()**方法類(lèi)似于Object類(lèi)中的notifyAll()方法。

應(yīng)用場(chǎng)景:Condition是一個(gè)多線(xiàn)程間協(xié)調(diào)通信的工具類(lèi),使得某個(gè),或者某些線(xiàn)程一起等待某個(gè)條件(Condition),只有當(dāng)該條件具備( signal 或者 signalAll方法被帶調(diào)用)時(shí) ,這些等待線(xiàn)程才會(huì)被喚醒,從而重新?tīng)?zhēng)奪鎖。

package com.wwj.javabase.thread.order;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author wwj
 * 使用Condition(條件變量)實(shí)現(xiàn)線(xiàn)程按順序運(yùn)行
 */

public class ThreadConditionDemo {

    private static Lock lock = new ReentrantLock();
    private static Condition condition1 = lock.newCondition();
    private static Condition condition2 = lock.newCondition();

    /**
     * 為什么要加這兩個(gè)標(biāo)識(shí)狀態(tài)?
     * 如果沒(méi)有狀態(tài)標(biāo)識(shí),當(dāng)t1已經(jīng)運(yùn)行完了t2才運(yùn)行,t2在等待t1喚醒導(dǎo)致t2永遠(yuǎn)處于等待狀態(tài)
     */

    private static Boolean t1Run = false;
    private static Boolean t2Run = false;

    public static void main(String[] args) {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println("產(chǎn)品經(jīng)理規(guī)劃新需求");
                t1Run = true;
                condition1.signal();
                lock.unlock();
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    if(!t1Run){
                        System.out.println("開(kāi)發(fā)人員先休息會(huì)...");
                        condition1.await();
                    }
                    System.out.println("開(kāi)發(fā)人員開(kāi)發(fā)新需求功能");
                    t2Run = true;
                    condition2.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock.unlock();
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    if(!t2Run){
                        System.out.println("測(cè)試人員先休息會(huì)...");
                        condition2.await();
                    }
                    System.out.println("測(cè)試人員測(cè)試新功能");
                    lock.unlock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測(cè)試人員來(lái)上班了...");
        thread3.start();
        System.out.println("產(chǎn)品經(jīng)理來(lái)上班了...");
        thread1.start();
        System.out.println("開(kāi)發(fā)人員來(lái)上班了...");
        thread2.start();
    }
}

運(yùn)行結(jié)果:這里輸出會(huì)有很多種順序,主要是因?yàn)榫€(xiàn)程進(jìn)入的順序,造成鎖住線(xiàn)程的順序不一致

早上:
測(cè)試人員來(lái)上班了...
產(chǎn)品經(jīng)理來(lái)上班了...
開(kāi)發(fā)人員來(lái)上班了...
測(cè)試人員先休息會(huì)...
產(chǎn)品經(jīng)理規(guī)劃新需求
開(kāi)發(fā)人員開(kāi)發(fā)新需求功能
測(cè)試人員測(cè)試新功能
6.使用線(xiàn)程的CountDownLatch(倒計(jì)數(shù))方法

CountDownLatch:位于java.util.concurrent包下,利用它可以實(shí)現(xiàn)類(lèi)似計(jì)數(shù)器的功能。

應(yīng)用場(chǎng)景:比如有一個(gè)任務(wù)C,它要等待其他任務(wù)A,B執(zhí)行完畢之后才能執(zhí)行,此時(shí)就可以利用CountDownLatch來(lái)實(shí)現(xiàn)這種功能了。

package com.wwj.javabase.thread.order;

import java.util.concurrent.CountDownLatch;

/**
 * @author wwj
 * 通過(guò)CountDownLatch(倒計(jì)數(shù))使線(xiàn)程按順序執(zhí)行
 */

public class ThreadCountDownLatchDemo {

    /**
     * 用于判斷線(xiàn)程一是否執(zhí)行,倒計(jì)時(shí)設(shè)置為1,執(zhí)行后減1
     */

    private static CountDownLatch c1 = new CountDownLatch(1);

    /**
     * 用于判斷線(xiàn)程二是否執(zhí)行,倒計(jì)時(shí)設(shè)置為1,執(zhí)行后減1
     */

    private static CountDownLatch c2 = new CountDownLatch(1);

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("產(chǎn)品經(jīng)理規(guī)劃新需求");
                //對(duì)c1倒計(jì)時(shí)-1
                c1.countDown();
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //等待c1倒計(jì)時(shí),計(jì)時(shí)為0則往下運(yùn)行
                    c1.await();
                    System.out.println("開(kāi)發(fā)人員開(kāi)發(fā)新需求功能");
                    //對(duì)c2倒計(jì)時(shí)-1
                    c2.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //等待c2倒計(jì)時(shí),計(jì)時(shí)為0則往下運(yùn)行
                    c2.await();
                    System.out.println("測(cè)試人員測(cè)試新功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測(cè)試人員來(lái)上班了...");
        thread3.start();
        System.out.println("產(chǎn)品經(jīng)理來(lái)上班了...");
        thread1.start();
        System.out.println("開(kāi)發(fā)人員來(lái)上班了...");
        thread2.start();
    }
}

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

早上:
測(cè)試人員來(lái)上班了...
產(chǎn)品經(jīng)理來(lái)上班了...
開(kāi)發(fā)人員來(lái)上班了...
產(chǎn)品經(jīng)理規(guī)劃新需求
開(kāi)發(fā)人員開(kāi)發(fā)新需求功能
測(cè)試人員測(cè)試新功能
7.使用CyclicBarrier(回環(huán)柵欄)實(shí)現(xiàn)線(xiàn)程按順序運(yùn)行

CyclicBarrier(回環(huán)柵欄):通過(guò)它可以實(shí)現(xiàn)讓一組線(xiàn)程等待至某個(gè)狀態(tài)之后再全部同時(shí)執(zhí)行。叫做回環(huán)是因?yàn)楫?dāng)所有等待線(xiàn)程都被釋放以后,CyclicBarrier可以被重用。我們暫且把這個(gè)狀態(tài)就叫做barrier,當(dāng)調(diào)用await()方法之后,線(xiàn)程就處于barrier了。

應(yīng)用場(chǎng)景:公司組織春游,等待所有的員工到達(dá)集合地點(diǎn)才能出發(fā),每個(gè)人到達(dá)后進(jìn)入barrier狀態(tài)。都到達(dá)后,喚起大家一起出發(fā)去旅行。

package com.wwj.javabase.thread.order;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @author wwj
 * 使用CyclicBarrier(回環(huán)柵欄)實(shí)現(xiàn)線(xiàn)程按順序運(yùn)行
 */

public class CyclicBarrierDemo {

    static CyclicBarrier barrier1 = new CyclicBarrier(2);
    static CyclicBarrier barrier2 = new CyclicBarrier(2);

    public static void main(String[] args) {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("產(chǎn)品經(jīng)理規(guī)劃新需求");
                    //放開(kāi)柵欄1
                    barrier1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //放開(kāi)柵欄1
                    barrier1.await();
                    System.out.println("開(kāi)發(fā)人員開(kāi)發(fā)新需求功能");
                    //放開(kāi)柵欄2
                    barrier2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //放開(kāi)柵欄2
                    barrier2.await();
                    System.out.println("測(cè)試人員測(cè)試新功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測(cè)試人員來(lái)上班了...");
        thread3.start();
        System.out.println("產(chǎn)品經(jīng)理來(lái)上班了...");
        thread1.start();
        System.out.println("開(kāi)發(fā)人員來(lái)上班了...");
        thread2.start();
    }
}

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

早上:
測(cè)試人員來(lái)上班了...
產(chǎn)品經(jīng)理來(lái)上班了...
開(kāi)發(fā)人員來(lái)上班了...
產(chǎn)品經(jīng)理規(guī)劃新需求
開(kāi)發(fā)人員開(kāi)發(fā)新需求功能
測(cè)試人員測(cè)試新功能
8.使用Sephmore(信號(hào)量)實(shí)現(xiàn)線(xiàn)程按順序運(yùn)行

Sephmore(信號(hào)量):Semaphore是一個(gè)計(jì)數(shù)信號(hào)量,從概念上將,Semaphore包含一組許可證,如果有需要的話(huà),每個(gè)acquire()方法都會(huì)阻塞,直到獲取一個(gè)可用的許可證,每個(gè)release()方法都會(huì)釋放持有許可證的線(xiàn)程,并且歸還Semaphore一個(gè)可用的許可證。然而,實(shí)際上并沒(méi)有真實(shí)的許可證對(duì)象供線(xiàn)程使用,Semaphore只是對(duì)可用的數(shù)量進(jìn)行管理維護(hù)。

acquire():當(dāng)前線(xiàn)程嘗試去阻塞的獲取1個(gè)許可證,此過(guò)程是阻塞的,當(dāng)前線(xiàn)程獲取了1個(gè)可用的許可證,則會(huì)停止等待,繼續(xù)執(zhí)行。

release():當(dāng)前線(xiàn)程釋放1個(gè)可用的許可證。

應(yīng)用場(chǎng)景:Semaphore可以用來(lái)做流量分流,特別是對(duì)公共資源有限的場(chǎng)景,比如數(shù)據(jù)庫(kù)連接。假設(shè)有這個(gè)的需求,讀取幾萬(wàn)個(gè)文件的數(shù)據(jù)到數(shù)據(jù)庫(kù)中,由于文件讀取是IO密集型任務(wù),可以啟動(dòng)幾十個(gè)線(xiàn)程并發(fā)讀取,但是數(shù)據(jù)庫(kù)連接數(shù)只有10個(gè),這時(shí)就必須控制最多只有10個(gè)線(xiàn)程能夠拿到數(shù)據(jù)庫(kù)連接進(jìn)行操作。這個(gè)時(shí)候,就可以使用Semaphore做流量控制。

package com.wwj.javabase.thread.order;

import java.util.concurrent.Semaphore;
/**
 * @author wwj
 * 使用Sephmore(信號(hào)量)實(shí)現(xiàn)線(xiàn)程按順序運(yùn)行
 */

public class SemaphoreDemo {
    private static Semaphore semaphore1 = new Semaphore(1);
    private static Semaphore semaphore2 = new Semaphore(1);
    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("產(chǎn)品經(jīng)理規(guī)劃新需求");
                semaphore1.release();
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore1.acquire();
                    System.out.println("開(kāi)發(fā)人員開(kāi)發(fā)新需求功能");
                    semaphore2.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore2.acquire();
                    thread2.join();
                    semaphore2.release();
                    System.out.println("測(cè)試人員測(cè)試新功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測(cè)試人員來(lái)上班了...");
        thread3.start();
        System.out.println("產(chǎn)品經(jīng)理來(lái)上班了...");
        thread1.start();
        System.out.println("開(kāi)發(fā)人員來(lái)上班了...");
        thread2.start();
    }
}

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

早上:
測(cè)試人員來(lái)上班了...
產(chǎn)品經(jīng)理來(lái)上班了...
開(kāi)發(fā)人員來(lái)上班了...
產(chǎn)品經(jīng)理規(guī)劃新需求
開(kāi)發(fā)人員開(kāi)發(fā)新需求功能
測(cè)試人員測(cè)試新功能
總結(jié)

看完了這么多種方法,是不是對(duì)多線(xiàn)程有了更深入的了解呢?不妨自己試試吧(代碼拷貝均可運(yùn)行)

使用的場(chǎng)景還有很多,根據(jù)開(kāi)發(fā)需求場(chǎng)景,選擇合適的方法,達(dá)到事半功倍的效果。


*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀(guān)點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。

接地電阻相關(guān)文章:接地電阻測(cè)試方法




關(guān)鍵詞:

相關(guān)推薦

技術(shù)專(zhuān)區(qū)

關(guān)閉