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

5種必會的Java異步調用轉同步的方法你會幾種

 更新時間:2020年12月30日 10:29:13   作者:程序員Sunny  
這篇文章主要介紹了5種必會的Java異步調用轉同步的方法你會幾種,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

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

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

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

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

0.構造一個異步調用

首先,寫demo需要先寫基礎設施,這里的話主要是需要構造一個異步調用模型。異步調用類:

public class AsyncCall {

  private Random random = new Random(System.currentTimeMillis());

  private ExecutorService tp = Executors.newSingleThreadExecutor();

  //demo1,2,4,5調用方法
  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調用方法
  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();

  }
}

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

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

demo的基類:

public abstract class BaseDemo {

  protected AsyncCall asyncCall = new AsyncCall();

  public abstract void callback(long response);

  public void call(){
    System.out.println("發(fā)起調用");
    asyncCall.call(this);
    System.out.println("調用返回");
  }
}

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

1. 使用wait和notify方法

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

public class Demo1 extends BaseDemo{

  private final Object lock = new Object();

  @Override
  public void callback(long response) {
    System.out.println("得到結果");
    System.out.println(response);
    System.out.println("調用結束");

    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("主線程內容");

  }
}

可以看到在發(fā)起調用后,主線程利用wait進行阻塞,等待回調中調用notify或者notifyAll方法來進行喚醒。注意,和大家認知的一樣,這里wait和notify都是需要先獲得對象的鎖的。在主線程中最后我們打印了一個內容,這也是用來驗證實驗結果的,如果沒有wait和notify,主線程內容會緊隨調用內容立刻打?。欢裎覀兩厦娴拇a,主線程內容會一直等待回調函數調用結束才會進行打印。

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

發(fā)起調用
調用返回
主線程內容
得到結果
1
調用結束

而使用了同步操作后:

發(fā)起調用
調用返回
得到結果
9
調用結束
主線程內容

2. 使用條件鎖

和方法一的原理類似:

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("得到結果");
    System.out.println(response);
    System.out.println("調用結束");
    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("主線程內容");
  }
}

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

3. Future

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

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ā)起調用");
    Future<Long> future = demo3.call();
    System.out.println("返回結果");

    while (!future.isDone() && !future.isCancelled());

    try {
      System.out.println(future.get());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }

    System.out.println("主線程內容");

  }
}

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

public void shutdown(){

    tp.shutdown();

  }

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

4. 使用CountDownLatch

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

public class Demo4 extends BaseDemo{

  private final CountDownLatch countDownLatch = new CountDownLatch(1);

  @Override
  public void callback(long response) {

    System.out.println("得到結果");
    System.out.println(response);
    System.out.println("調用結束");

    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("主線程內容");

  }
}

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

5. 使用CyclicBarrier

CyclicBarrier的情況和CountDownLatch有些類似:

public class Demo5 extends BaseDemo{

  private CyclicBarrier cyclicBarrier = new CyclicBarrier(2);


  @Override
  public void callback(long response) {

    System.out.println("得到結果");
    System.out.println(response);
    System.out.println("調用結束");

    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("主線程內容");

  }
}

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

總結

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

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

到此這篇關于5種必會的Java異步調用轉同步的方法你會幾種的文章就介紹到這了,更多相關Java異步調用轉同步內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 啟動springboot應用因未配置數據庫報錯的解決方案

    啟動springboot應用因未配置數據庫報錯的解決方案

    這篇文章主要介紹了啟動springboot應用因未配置數據庫報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • springboot利用@Aspect實現日志工具類的詳細代碼

    springboot利用@Aspect實現日志工具類的詳細代碼

    這篇文章主要介紹了springboot利用@Aspect實現日志工具類,通過實例代碼介紹了導包及在啟動類上進行注解自動掃描的方法,需要的朋友可以參考下
    2022-03-03
  • java序列化與反序列化的使用方法匯總

    java序列化與反序列化的使用方法匯總

    序列化是一種對象持久化的手段,普遍應用在網絡傳輸、RMI等場景中,這篇文章主要給大家總結介紹了關于java序列化與反序列化的使用方法,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2021-07-07
  • Java實現簡單登陸界面

    Java實現簡單登陸界面

    這篇文章主要為大家詳細介紹了Java實現簡單登陸界面,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Springboot整合Mybatis傳值的常用方式總結

    Springboot整合Mybatis傳值的常用方式總結

    今天給大家?guī)淼氖顷P于Springboot的相關知識,文章圍繞著Springboot整合Mybatis傳值的常用方式展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • java中的構造函數什么時候被調用執(zhí)行

    java中的構造函數什么時候被調用執(zhí)行

    這篇文章主要介紹了java中的構造函數什么時候被調用執(zhí)行問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • JAVA中尋找最大的K個數解法

    JAVA中尋找最大的K個數解法

    尋找最大的K個數,這個是面試中比較常見的一道題,網上也有很多例子,在這里是比較傳統的解法
    2014-04-04
  • Java線程并發(fā)訪問代碼分析

    Java線程并發(fā)訪問代碼分析

    這篇文章主要介紹了Java線程并發(fā)訪問代碼分析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • Java中實現日志記錄的方案總結

    Java中實現日志記錄的方案總結

    在平時使用到一些軟件中,比如某寶或者某書,通過記錄用戶的行為來構建和分析用戶的行為數據,這就需要使用到日志系統來存儲或者記錄數據,小編為大家整理了幾種Java日志方案,希望對大家有所幫助
    2024-12-12
  • SpringBoot集成Kafka的實現示例

    SpringBoot集成Kafka的實現示例

    本文主要介紹了SpringBoot集成Kafka的實現示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-01-01

最新評論

英吉沙县| 习水县| 蕲春县| 陵川县| 芜湖县| 金平| 莆田市| 秦皇岛市| 宁乡县| 青岛市| 靖安县| 巫溪县| 中卫市| 南开区| 喜德县| 鄯善县| 禄丰县| 罗平县| 蓝田县| 台东县| 宁城县| 宝山区| 定边县| 长丰县| 凌海市| 兴仁县| 大宁县| 温宿县| 福安市| 施甸县| 富裕县| 博爱县| 南宫市| 缙云县| 孝昌县| 罗甸县| 吉隆县| 平顺县| 胶南市| 石棉县| 龙陵县|