一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術(shù)及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 5種必會的Java異步調(diào)用轉(zhuǎn)同步的方法你會幾種

5種必會的Java異步調(diào)用轉(zhuǎn)同步的方法你會幾種

2021-11-19 14:52程序員Sunny Java教程

這篇文章主要介紹了5種必會的Java異步調(diào)用轉(zhuǎn)同步的方法你會幾種,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

Sunny先來說一下對異步和同步的理解:

  • 同步調(diào)用:調(diào)用方在調(diào)用過程中,持續(xù)等待返回結(jié)果。
  • 異步調(diào)用:調(diào)用方在調(diào)用過程中,不直接等待返回結(jié)果,而是執(zhí)行其他任務,結(jié)果返回形式通常為回調(diào)函數(shù)。

其實,兩者的區(qū)別還是很明顯的,這里也不再細說,我們主要來說一下Java如何將異步調(diào)用轉(zhuǎn)為同步。換句話說,就是需要在異步調(diào)用過程中,持續(xù)阻塞至獲得調(diào)用結(jié)果。
不賣關(guān)子,先列出五種方法,然后一一舉例說明:

  • 使用wait和notify方法
  • 使用條件鎖
  • Future
  • 使用CountDownLatch
  • 使用CyclicBarrier

0.構(gòu)造一個異步調(diào)用

首先,寫demo需要先寫基礎設施,這里的話主要是需要構(gòu)造一個異步調(diào)用模型。異步調(diào)用類:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class AsyncCall {
 
  private Random random = new Random(System.currentTimeMillis());
 
  private ExecutorService tp = Executors.newSingleThreadExecutor();
 
  //demo1,2,4,5調(diào)用方法
  public void call(BaseDemo demo){
 
    new Thread(()->{
      long res = random.nextInt(10);
 
      try {
        Thread.sleep(res*1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
 
      demo.callback(res);
    }).start();
 
 
  }
 
  //demo3調(diào)用方法
  public Future<Long> futureCall(){
 
    return tp.submit(()-> {
      long res = random.nextInt(10);
 
      try {
        Thread.sleep(res*1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return res;
    });
 
  }
 
  public void shutdown(){
 
    tp.shutdown();
 
  }
}

我們主要關(guān)心call方法,這個方法接收了一個demo參數(shù),并且開啟了一個線程,在線程中執(zhí)行具體的任務,并利用demo的callback方法進行回調(diào)函數(shù)的調(diào)用。大家注意到了這里的返回結(jié)果就是一個[0,10)的長整型,并且結(jié)果是幾,就讓線程sleep多久——這主要是為了更好地觀察實驗結(jié)果,模擬異步調(diào)用過程中的處理時間。

至于futureCall和shutdown方法,以及線程池tp都是為了demo3利用Future來實現(xiàn)做準備的。

demo的基類:

?
1
2
3
4
5
6
7
8
9
10
11
12
public abstract class BaseDemo {
 
  protected AsyncCall asyncCall = new AsyncCall();
 
  public abstract void callback(long response);
 
  public void call(){
    System.out.println("發(fā)起調(diào)用");
    asyncCall.call(this);
    System.out.println("調(diào)用返回");
  }
}

BaseDemo非常簡單,里面包含一個異步調(diào)用類的實例,另外有一個call方法用于發(fā)起異步調(diào)用,當然還有一個抽象方法callback需要每個demo去實現(xiàn)的——主要在回調(diào)中進行相應的處理來達到異步調(diào)用轉(zhuǎn)同步的目的。

1. 使用wait和notify方法

這個方法其實是利用了鎖機制,直接貼代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Demo1 extends BaseDemo{
 
  private final Object lock = new Object();
 
  @Override
  public void callback(long response) {
    System.out.println("得到結(jié)果");
    System.out.println(response);
    System.out.println("調(diào)用結(jié)束");
 
    synchronized (lock) {
      lock.notifyAll();
    }
 
  }
 
  public static void main(String[] args) {
 
    Demo1 demo1 = new Demo1();
 
    demo1.call();
 
    synchronized (demo1.lock){
      try {
        demo1.lock.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
 
    System.out.println("主線程內(nèi)容");
 
  }
}

可以看到在發(fā)起調(diào)用后,主線程利用wait進行阻塞,等待回調(diào)中調(diào)用notify或者notifyAll方法來進行喚醒。注意,和大家認知的一樣,這里wait和notify都是需要先獲得對象的鎖的。在主線程中最后我們打印了一個內(nèi)容,這也是用來驗證實驗結(jié)果的,如果沒有wait和notify,主線程內(nèi)容會緊隨調(diào)用內(nèi)容立刻打印;而像我們上面的代碼,主線程內(nèi)容會一直等待回調(diào)函數(shù)調(diào)用結(jié)束才會進行打印。

沒有使用同步操作的情況下,打印結(jié)果:

發(fā)起調(diào)用
調(diào)用返回
主線程內(nèi)容
得到結(jié)果
1
調(diào)用結(jié)束

而使用了同步操作后:

發(fā)起調(diào)用
調(diào)用返回
得到結(jié)果
9
調(diào)用結(jié)束
主線程內(nèi)容

2. 使用條件鎖

和方法一的原理類似:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Demo2 extends BaseDemo {
 
  private final Lock lock = new ReentrantLock();
  private final Condition con = lock.newCondition();
 
  @Override
  public void callback(long response) {
 
    System.out.println("得到結(jié)果");
    System.out.println(response);
    System.out.println("調(diào)用結(jié)束");
    lock.lock();
    try {
      con.signal();
    }finally {
      lock.unlock();
    }
 
  }
 
  public static void main(String[] args) {
 
    Demo2 demo2 = new Demo2();
 
    demo2.call();
 
    demo2.lock.lock();
 
    try {
      demo2.con.await();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }finally {
      demo2.lock.unlock();
    }
    System.out.println("主線程內(nèi)容");
  }
}

基本上和方法一沒什么區(qū)別,只是這里使用了條件鎖,兩者的鎖機制有所不同。

3. Future

使用Future的方法和之前不太一樣,我們調(diào)用的異步方法也不一樣。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Demo3{
 
  private AsyncCall asyncCall = new AsyncCall();
 
  public Future<Long> call(){
 
    Future<Long> future = asyncCall.futureCall();
 
    asyncCall.shutdown();
 
    return future;
 
  }
 
  public static void main(String[] args) {
 
    Demo3 demo3 = new Demo3();
 
    System.out.println("發(fā)起調(diào)用");
    Future<Long> future = demo3.call();
    System.out.println("返回結(jié)果");
 
    while (!future.isDone() && !future.isCancelled());
 
    try {
      System.out.println(future.get());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }
 
    System.out.println("主線程內(nèi)容");
 
  }
}

我們調(diào)用futureCall方法,方法中會想線程池tp提交一個Callable,然后返回一個Future,這個Future就是我們demo3中call中得到的,得到future對象之后就可以關(guān)閉線程池啦,調(diào)用asyncCall的shutdown方法。關(guān)于關(guān)閉線程池這里有一點需要注意,我們回過頭來看看asyncCall的shutdown方法:

?
1
2
3
4
5
public void shutdown(){
 
    tp.shutdown();
 
  }

發(fā)現(xiàn)只是簡單調(diào)用了線程池的shutdown方法,然后我們說注意的點,這里最好不要用tp的shutdownNow方法,該方法會試圖去中斷線程中中正在執(zhí)行的任務;也就是說,如果使用該方法,有可能我們的future所對應的任務將被中斷,無法得到執(zhí)行結(jié)果。
然后我們關(guān)注主線程中的內(nèi)容,主線程的阻塞由我們自己來實現(xiàn),通過future的isDone和isCancelled來判斷執(zhí)行狀態(tài),一直到執(zhí)行完成或被取消。隨后,我們打印get到的結(jié)果。

4. 使用CountDownLatch

使用CountDownLatch或許是日常編程中最常見的一種了,也感覺是相對優(yōu)雅的一種:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Demo4 extends BaseDemo{
 
  private final CountDownLatch countDownLatch = new CountDownLatch(1);
 
  @Override
  public void callback(long response) {
 
    System.out.println("得到結(jié)果");
    System.out.println(response);
    System.out.println("調(diào)用結(jié)束");
 
    countDownLatch.countDown();
 
  }
 
  public static void main(String[] args) {
 
    Demo4 demo4 = new Demo4();
 
    demo4.call();
 
    try {
      demo4.countDownLatch.await();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
 
    System.out.println("主線程內(nèi)容");
 
  }
}

正如大家平時使用的那樣,此處在主線程中利用CountDownLatch的await方法進行阻塞,在回調(diào)中利用countDown方法來使得其他線程await的部分得以繼續(xù)運行。
當然,這里和demo1和demo2中都一樣,主線程中阻塞的部分,都可以設置一個超時時間,超時后可以不再阻塞。

5. 使用CyclicBarrier

CyclicBarrier的情況和CountDownLatch有些類似:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Demo5 extends BaseDemo{
 
  private CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
 
 
  @Override
  public void callback(long response) {
 
    System.out.println("得到結(jié)果");
    System.out.println(response);
    System.out.println("調(diào)用結(jié)束");
 
    try {
      cyclicBarrier.await();
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (BrokenBarrierException e) {
      e.printStackTrace();
    }
 
  }
 
  public static void main(String[] args) {
 
    Demo5 demo5 = new Demo5();
 
    demo5.call();
 
    try {
      demo5.cyclicBarrier.await();
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (BrokenBarrierException e) {
      e.printStackTrace();
    }
 
    System.out.println("主線程內(nèi)容");
 
  }
}

大家注意一下,CyclicBarrier和CountDownLatch僅僅只是類似,兩者還是有一定區(qū)別的。比如,一個可以理解為做加法,等到加到這個數(shù)字后一起運行;一個則是減法,減到0繼續(xù)運行。一個是可以重復計數(shù)的;另一個不可以等等等等。
另外,使用CyclicBarrier的時候要注意兩點。第一點,初始化的時候,參數(shù)數(shù)字要設為2,因為異步調(diào)用這里是一個線程,而主線程是一個線程,兩個線程都await的時候才能繼續(xù)執(zhí)行,這也是和CountDownLatch區(qū)別的部分。第二點,也是關(guān)于初始化參數(shù)的數(shù)值的,和這里的demo無關(guān),在平時編程的時候,需要比較小心,如果這個數(shù)值設置得很大,比線程池中的線程數(shù)都大,那么就很容易引起死鎖了。

總結(jié)

綜上,就是本次需要說的幾種方法了。事實上,所有的方法都是同一個原理,也就是在調(diào)用的線程中進行阻塞等待結(jié)果,而在回調(diào)中函數(shù)中進行阻塞狀態(tài)的解除。

源碼地址:https://gitee.com/sunnymore/asyncToSync

到此這篇關(guān)于5種必會的Java異步調(diào)用轉(zhuǎn)同步的方法你會幾種的文章就介紹到這了,更多相關(guān)Java異步調(diào)用轉(zhuǎn)同步內(nèi)容請搜索服務器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.jianshu.com/p/f00aa6f66281

延伸 · 閱讀

精彩推薦
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發(fā)項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經(jīng)有好久沒有升過級了。升級完畢重啟之后,突然發(fā)現(xiàn)好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程xml與Java對象的轉(zhuǎn)換詳解

    xml與Java對象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程Java實現(xiàn)搶紅包功能

    Java實現(xiàn)搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現(xiàn)搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發(fā)現(xiàn)了對于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
主站蜘蛛池模板: 九九99九九精彩 | 精品国产欧美一区二区五十路 | 成人福利免费视频 | 第一次出血videos | 97菊爱网 | 国产xxxxxx久色视频在 | 麻豆网站视频国产在线观看 | 99视频都是精品热在线播放 | 免费一级欧美片在线观免看 | 亚洲国产资源 | 91tv破解版不限次数 | 人人揉揉香蕉 | 隔壁的漂亮邻居hd中文 | 欧美久久天天综合香蕉伊 | 日韩视频免费观看 | 俄罗斯15一16处交 | futa文| 日本 在线播放 | 黄网久久 | 扒开双腿羞辱调教play视频 | 99午夜高清在线视频在观看 | katsuniav在线播放 | 成人精品亚洲人成在线 | 美女污视频在线观看 | 丝袜美女被艹 | 华人在线视频 | 美女下面被cao出水 美女污视频 | freesex1718处xx | 日韩在线免费看 | 国产精品嫩草影院一二三区入口 | a级黄色网 | 国产在线视频资源 | 幻女free性俄罗斯第一次摘花 | 亚洲午夜精品久久久久久成年 | 国产haodiaose最新 | 国产成人久久精品区一区二区 | 久久无码人妻AV精品一区 | a一区二区三区视频 | 色图18p | 按摩院已婚妇女中文字幕 | 91桃色污|