日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > > 架構師社區(qū)
[導讀]多線程并發(fā)執(zhí)行?線程之間通信?這是我偶爾聽到我同事做面試官時問的一道題,感覺很有意思,發(fā)出來大家和大家討論下。

詳解一道京東面試題

多線程并發(fā)執(zhí)行?線程之間通信?這是我偶爾聽到我同事做面試官時問的一道題,感覺很有意思,發(fā)出來大家和大家討論下

面試題目描述

現(xiàn)在呢,我們有三個接口,就叫他A,B,C吧,這三個接口都是查詢某個人征信信息的,必須同時返回true,我們才認為這個人的征信合格,如果其中某一個返回false的話,就表明這個人的征信不合格,如果是你,你會怎么設計怎么寫這個代碼呢?

第一次思考

首先,一定是并發(fā)執(zhí)行,假如說A接口執(zhí)行3秒,B接口執(zhí)行5秒,C接口執(zhí)行8秒的話

  • 串行執(zhí)行: 3+5+8 = 16秒
  • 并發(fā)執(zhí)行: 8=8秒 (時間最久的那個接口執(zhí)行的時間就是這三個接口的執(zhí)行總時間)

熟悉的感覺,多線程執(zhí)行任務,我在第二章文章實戰(zhàn)!xhJaver竟然用線程池優(yōu)化了。。。有提過怎么寫,感興趣的讀者可以回去看一下,不過我在這里再寫一下,話不多說來看下代碼

并發(fā)代碼

建議用PC端查看,所有代碼都可直接復制運行,代碼中重要的點都有詳細注釋

  1. 首先,我們先定義這三個接口
public class DoService { //設置A?B?C接口的返回值,b接口設置的是false private static Boolean?flagA?= true; private static Boolean?flagB?= false; private static Boolean?flagC?= true; public static boolean A(){ long start?=?System.currentTimeMillis(); try {
????????????Thread.sleep(3000L);
????????} catch (InterruptedException?e)?{
????????????System.out.println("a被打斷??耗時" +?(System.currentTimeMillis()?-?start));
???????????????e.printStackTrace();
????????}
????????System.out.println("a耗時??"+(System.currentTimeMillis()?-?start)); return flagA;
????} public static boolean B() { long start?=?System.currentTimeMillis(); try {
????????????Thread.sleep(5000L);
????????} catch (InterruptedException?e)?{
????????????System.out.println("b被打斷??耗時" +?(System.currentTimeMillis()?-?start));
????????????e.printStackTrace();
????????}
????????System.out.println("b耗時??"+(System.currentTimeMillis()?-?start)); return flagB;
????} public static boolean C() { long start?=?System.currentTimeMillis(); try {
????????????Thread.sleep(8000L);
????????} catch (InterruptedException?e)?{
????????????System.out.println("c被打斷??耗時" +?(System.currentTimeMillis()?-?start));
????????????e.printStackTrace();
????????}
????????System.out.println("c耗時??"+(System.currentTimeMillis()?-?start)); return flagC;
????}
}
  1. 其次 我們先創(chuàng)造一個Task 任務類
public class Task implements Callable<Boolean> { private String?taskName; private Integer?i; public Task(String?taskName,int i){ this.taskName?=taskName; this.i?=?i;
????} @Override public Boolean call() throws Exception { //?標記?返回值,代表這個接口是否執(zhí)行成功 Boolean?flag?= false; //記錄接口名字 String?serviceName?= null; //根據(jù)i的值來判斷調用哪個接口 if (i==1){
????????????flag???=????DoService.A();
????????????serviceName="A";
????????} if (i==2){
????????????flag???=????DoService.B();
????????????serviceName="B";
????????} if (i==3){
????????????flag???=????DoService.C();
????????????serviceName="C";
????????}
????????System.out.println("當前線程是:?"+Thread.currentThread().getName()+"正在處理的任務是:?"+this.taskName+"調用的接口是:?"+serviceName); return flag;
????}
}
  1. 最后,我們定義一個測試類
class Test { public static void main(String[]?args) throws ExecutionException,?InterruptedException { //創(chuàng)建一個包含三個線程的線程池 ExecutorService?executorService?=?Executors.newFixedThreadPool(3); //事先準備好儲存結果的list集合 List< Future>?list?= new ArrayList<>(); //開始計時 long start?=?System.currentTimeMillis(); for (int i=1;i<4;i++){
????????????Task?task?= new Task("任務"+i,i); //將每個任務提交到線程池中,并且得到這個線程的執(zhí)行結果 Futureresult?=?executorService.submit(task);
????????????list.add(result);
????????} //記得把線程池關閉 executorService.shutdown(); //定義一個變量?0 int count?= 0;
????????System.out.println("等待處理結果。。。"); for (int i=0;i//得到處理的結果?線程阻塞,如果線程沒有處理完就一直阻塞 Boolean?flag?=?result.get(); //如果這個接口返回true,那么count就++ if (flag){
????????????????count++;
????????????}
????????}
????????System.out.println("線程池+結果處理時間:"+?(System.currentTimeMillis()?-?start)); //如果count數(shù)量為3,那么三個就都為true,代表這個人征信沒問題 if (count==3){
????????????System.out.println("合格");
????????}else {//否則,就是有問題 System.out.println("不合格");
????????}
????}
}
  1. 我們看下輸出結果
等待處理結果。。。
a耗時 3000 當前線程是:?pool-1-thread-1正在處理的任務是:?任務1調用的接口是:?A
b耗時 5000 當前線程是:?pool-1-thread-2正在處理的任務是:?任務2調用的接口是:?B
c耗時 8000 當前線程是:?pool-1-thread-3正在處理的任務是:?任務3調用的接口是:?C
線程池+結果處理時間:8008 不合格
  • 我們運行的時候會發(fā)現(xiàn),它的輸出結果的順序如下 1 2 3 4 5 京東這道面試題你會嗎?我們圖中的2,3,4是再線程池內開了三個線程執(zhí)行的,他們之間相隔一段時間才出現(xiàn)的,因為每個接口都有執(zhí)行時間

程序運行后,“標記2”是3秒后出現(xiàn),“標記三”是5秒后出現(xiàn),“標記4”是8秒后出現(xiàn)

其實4和5相差時間很短,幾乎是同時出現(xiàn)的,因為4執(zhí)行完了就是主線程繼續(xù)執(zhí)行了

線程池+結果處理的時間一共是8秒,而每個接口分別執(zhí)行的時間是3秒,5秒,8秒,達到了我們所說的,多線程處理多個接口,總共耗時時間是耗時最長的接口的時間

和京東面試官探討

波哥說(我愛叫他波哥,東北人,說話則逗,幽默的人簡直就是人間瑰寶,其實我也蠻有趣的,就是沒人發(fā)現(xiàn)),你這程序不行啊,有個缺點,假如說,你這個A接口,耗時三秒,他返回了false,那么你另外兩個線程也不用執(zhí)行了,這個人的征信已經(jīng)不合格了,你需要判斷下,如果某一個線程執(zhí)行的任務返回了false,那么就及時中斷其他兩個線程

靈光乍現(xiàn)

上一次的代碼已經(jīng)實現(xiàn)了多線程執(zhí)行任務,可是這線程間通信怎么辦呢?怎么才能根據(jù)一個線程的執(zhí)行結果而打斷其他線程呢?我想到了以下幾點

  1. 共享變量 public static volatile boolean end = true;

  • 這個共享變量就代表是否結束三個線程的執(zhí)行 如果為true的話,代表結束,false的話代表不結束線程執(zhí)行
  • 計數(shù)器 public static AtomicInteger count =new AtomicInteger(0);

    • 每當每個線程執(zhí)行完的話,如果返回true,計數(shù)器就+1,當計數(shù)器變?yōu)?的時候,就代表這個人征信沒問題
  • 中斷方法 interrupt()

    • 我們會單獨開個線程一直循環(huán)檢測這個變量,當檢測到為true的時候,就會調用中斷方法中斷這三個線程
  • 阻塞線程 countDownLatch

    • 我們程序往下執(zhí)行需要獲取結果,獲取不到這個結果的話,就要一直等著。我們可以用這個線程阻塞的工具,一開始給他設置數(shù)量為1,當滿足繼續(xù)向下執(zhí)行的條件時,調用 countDownLatch.countDown();,在主線程那里 countDownLatch.await();一下這樣當檢測到數(shù)量為0的時候,主線程那里就繼續(xù)往下執(zhí)行了,話不多說,來看代碼

    代碼優(yōu)化

    建議用PC端查看,所有代碼都可直接復制運行,代碼中重要的點都有詳細注釋

    1. 首先,還是創(chuàng)建接口
    public class DoService { private static Boolean?flagA?= true; private static Boolean?flagB?= false; private static Boolean?flagC?= true; public static boolean A(){ long start?=?System.currentTimeMillis(); try {
    ????????????Thread.sleep(3000L);
    ????????} catch (InterruptedException?e)?{
    ????????????System.out.println("a被打斷??耗時" +?(System.currentTimeMillis()?-?start));
    ???????????????e.printStackTrace();
    ????????}
    ????????System.out.println("a耗時??"+(System.currentTimeMillis()?-?start)); return flagA;
    ????} public static boolean B() { long start?=?System.currentTimeMillis(); try {
    ????????????Thread.sleep(5000L);
    ????????} catch (InterruptedException?e)?{
    ????????????System.out.println("b被打斷??耗時" +?(System.currentTimeMillis()?-?start));
    ????????????e.printStackTrace();
    ????????}
    ????????System.out.println("b耗時??"+(System.currentTimeMillis()?-?start)); return flagB;
    ????} public static boolean C() { long start?=?System.currentTimeMillis(); try {
    ????????????Thread.sleep(8000L);
    ????????} catch (InterruptedException?e)?{
    ????????????System.out.println("c被打斷??耗時" +?(System.currentTimeMillis()?-?start));
    ????????????e.printStackTrace();
    ????????}
    ????????System.out.println("c耗時??"+(System.currentTimeMillis()?-?start)); return flagC;
    ????}
    }
    1. 創(chuàng)建任務
    public class Task implements Runnable { private String?name?; public Task(?String?name){ this.name?=?name;
    ????} @Override public void run() { boolean flag?= false;
    ????????String?serviceName?= null; if(this.name.equals("A")){
    ????????????serviceName?= "A";
    ?????????????flag?=?DoService.A();
    ????????} if(this.name.equals("B")){
    ????????????serviceName?= "B";
    ????????????flag?=?DoService.B();
    ????????} if(this.name.equals("C")){
    ???????????serviceName?= "C";
    ???????????flag?=?DoService.C();
    ???????} //如果有一個為false if (!flag){ //就把共享標志位置為false Test.end?= false;
    ????????}else { //計數(shù)器加一,到三的話就是三個都為true Test.count.incrementAndGet();
    ???????}
    ????????System.out.println("當前線程是:?"+Thread.currentThread().getName()+"正在處理的任務是:?"+this.name+"調用的接口是:?"+serviceName);
    ????}
    }
    1. 創(chuàng)建測試類
    class Test { //設置countDownLatch?里面計數(shù)為1, //?只調用一次countDownLatch.countDown就可以繼續(xù)執(zhí)行?countDownLatch.await(); //后面的代碼了,接觸阻塞 public static CountDownLatch?countDownLatch?= new CountDownLatch(1); //默認都為true,有一個線程為false了,那么就變?yōu)閒alse public static volatile boolean end?= true; //計數(shù)器,數(shù)字變?yōu)?的時候代表三個接口都返回true,線程安全的原子類 public static AtomicInteger?count?=new AtomicInteger(0); public static void main(String[]?args) throws InterruptedException { long start?=?System.currentTimeMillis(); //創(chuàng)建三個任務,分被調用A?B?C?接口 Task?taskA?= new Task("A");
    ????????Task?taskB?= new Task("B");
    ????????Task?taskC?= new Task("C"); //創(chuàng)建三個線程 Thread?tA?= new Thread(taskA);
    ????????Thread?tB?= new Thread(taskB);
    ????????Thread?tC?= new Thread(taskC); //開啟三個線程 tA.start();
    ????????tB.start();
    ????????tC.start(); //在開啟一個線程,這個線程就是單獨循環(huán)掃描這個共享變量的 new Thread(new Runnable()?{ @Override public void run() { //此線程一直循環(huán)判斷這個結束變量,如果為false的話,就代表有一個接口返回false,跳出,重點其他線程 while (true){ if (!end?){ //當這個共享變量為false時i表示,其他線程可以中斷了,所以就打斷他們執(zhí)行 tA.interrupt();
    ????????????????????????tB.interrupt();
    ????????????????????????tC.interrupt(); //如果某個線程被打斷的話,就表明不合格 System.out.println("不合格"); //countDownLatch?計數(shù)器減一 countDownLatch.countDown(); break;
    ????????????????????} if (Test.count.get()==3){
    ????????????????????????System.out.println("合格"); //countDownLatch?計數(shù)器減一 countDownLatch.countDown(); break;
    ????????????????????}
    ????????????????}
    ????????????}
    ????????}).start();
    ????????System.out.println(Thread.currentThread().getName()+"主線程開始掛起"); //阻塞主線程繼續(xù)執(zhí)行,等待其他線程計算完結果在執(zhí)行下去,countDownLatch中的計數(shù)為0時,就可以繼續(xù)執(zhí)行下去 countDownLatch.await();
    ????????System.out.println(Thread.currentThread().getName()+"?主線獲得結果后繼續(xù)執(zhí)行"+(System.currentTimeMillis()?-?start));
    ????}
    
    }
    1. 我們看下輸出結果
    main主線程開始掛起
    a耗時 3024 當前線程是:?Thread-0正在處理的任務是:?A調用的接口是:?A
    b耗時 5000 當前線程是:?Thread-1正在處理的任務是:?B調用的接口是:?B
    c被打斷??耗時5001 不合格
    java.lang.InterruptedException:?sleep?interrupted
    ?at?java.lang.Thread.sleep(Native?Method)
    ?at?com.xhj.concurrent.executor_05._02.DoService.C(DoService.java:41)
    ?at?com.xhj.concurrent.executor_05._02.Task.run(Task.java:30)
    ?at?java.lang.Thread.run(Thread.java:748)
    c耗時 5003 當前線程是:?Thread-2正在處理的任務是:?C調用的接口是:?C
    main?主線獲得結果后繼續(xù)執(zhí)行5014 
    • 我們運行的時候會發(fā)現(xiàn) 京東這道面試題你會嗎?

    由圖可見,我們首先就把主線程掛起,等待其他四個線程的處理結果,三個線程分別處理那三個接口,另外一個線程循環(huán)遍歷那個共享變量,當檢測到為false時,及時打斷其他線程,這樣的話,就解決了上面的那個問題。

    免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

    本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內容侵犯您的權益,請及時聯(lián)系本站刪除。
    換一批
    延伸閱讀

    LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

    關鍵字: 驅動電源

    在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅動性能的關鍵。

    關鍵字: 工業(yè)電機 驅動電源

    LED 驅動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

    關鍵字: 驅動電源 照明系統(tǒng) 散熱

    根據(jù)LED驅動電源的公式,電感內電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

    關鍵字: LED 設計 驅動電源

    電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

    關鍵字: 電動汽車 新能源 驅動電源

    在現(xiàn)代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質量和效率直接關系到城市的公共安全、居民生活質量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

    關鍵字: 發(fā)光二極管 驅動電源 LED

    LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

    關鍵字: LED 驅動電源 功率因數(shù)校正

    在LED照明技術日益普及的今天,LED驅動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

    關鍵字: LED照明技術 電磁干擾 驅動電源

    開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅動電源

    關鍵字: LED 驅動電源 開關電源

    LED驅動電源是把電源供應轉換為特定的電壓電流以驅動LED發(fā)光的電壓轉換器,通常情況下:LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

    關鍵字: LED 隧道燈 驅動電源
    關閉