目錄java
前置條件:構造一個異步調用編程
1、使用wait和notify方法併發
2、使用條件鎖dom
3、Future異步
4、使用CountDownLatchasync
5、使用CyclicBarrieride
總結函數
在Java併發編程中,常常會由於須要提升響應速度而將請求異步化,即將同步請求轉化爲異步處理,這是很天然能想到的一種處理方式。相反,在有些場景下也須要將異步處理轉化爲同步的方式。this
首先介紹一下同步調用和異步調用的概念:spa
同步調用:調用方在調用過程當中,持續等待返回結果。
異步調用:調用方在調用過程當中,不直接等待返回結果,而是執行其餘任務,結果返回形式一般爲回調函數。
其實,二者的區別仍是很明顯的,這裏也再也不細說,咱們主要來講一下Java如何將異步調用轉爲同步。換句話說,就是須要在異步調用過程當中,持續阻塞至得到調用結果。接下來將介紹5種Java併發編程中異步轉同步的方法。
首先,寫demo須要先寫基礎設施,這裏是須要構造一個異步調用模型。異步調用類:
import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class AsyncCall { private Random random = new Random(System.currentTimeMillis()); private ExecutorService tp = Executors.newSingleThreadExecutor(); public void call(AbstractBaseDemo demo) { new Thread(() -> { long res = random.nextInt(10); try { Thread.sleep(res * 1000); } catch (InterruptedException e) { e.printStackTrace(); } demo.callback(res); }).start(); } public Future<Long> futureCall() { return tp.submit(() -> { long res = random.nextInt(10); Thread.sleep(res * 1000); return res; }); } public void shutdown() { tp.shutdown(); } }
public abstract class AbstractBaseDemo { protected AsyncCall asyncCall = new AsyncCall(); public abstract void callback(long response); public void call() { System.out.println(Thread.currentThread().getName() + "發起調用"); asyncCall.call(this); System.out.println(Thread.currentThread().getName() + "調用返回"); } }
AbstractBaseDemo很是簡單,裏面包含一個異步調用類的實例,另外有一個call方法用於發起異步調用,固然還有一個抽象方法callback須要每一個demo去實現的——主要在回調中進行相應的處理來達到異步調用轉同步的目的。
這個方法實際上是利用了鎖機制,直接貼代碼:
public class ObjectWaitLockDemo extends AbstractBaseDemo { private final Object lock = new Object(); @Override public void callback(long response) { System.out.println(Thread.currentThread().getName() + "獲得結果"); System.out.println(response); System.out.println(Thread.currentThread().getName() + "調用結束"); synchronized (lock) { lock.notifyAll(); } } public static void main(String[] args) { ObjectWaitLockDemo demo = new ObjectWaitLockDemo(); demo.call(); synchronized (demo.lock) { try { demo.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "主線程內容"); } }
main發起調用 main調用返回 main主線程內容 Thread-0獲得結果 7 Thread-0調用結束
而使用了同步操做後:
main發起調用 main調用返回 Thread-0獲得結果 3 Thread-0調用結束 main主線程內容
和方法一的原理相似:
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockDemo extends AbstractBaseDemo { private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); @Override public void callback(long response) { System.out.println(Thread.currentThread().getName() + "獲得結果"); System.out.println(response); System.out.println(Thread.currentThread().getName() + "調用結束"); lock.lock(); try { condition.signal(); } finally { lock.unlock(); } } public static void main(String[] args) { ReentrantLockDemo demo = new ReentrantLockDemo(); demo.call(); demo.lock.lock(); try { demo.condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { demo.lock.unlock(); } System.out.println(Thread.currentThread().getName() + "主線程內容"); } }
基本上和方法一沒什麼區別,只是這裏使用了條件鎖,二者的鎖機制有所不一樣。
使用Future的方法和以前不太同樣,咱們調用的異步方法也不同。
import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; public class FutureDemo { private AsyncCall asyncCall = new AsyncCall(); public Future<Long> call() { Future<Long> future = asyncCall.futureCall(); asyncCall.shutdown(); return future; } public static void main(String[] args) { FutureDemo demo = new FutureDemo(); System.out.println(Thread.currentThread().getName() + "發起調用"); Future<Long> future = demo.call(); System.out.println(Thread.currentThread().getName() + "返回結果"); while (!future.isDone() && !future.isCancelled()); try { System.out.println(future.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "主線程內容"); } }
public void shutdown() { tp.shutdown(); }
使用CountDownLatch或許是平常編程中最多見的一種了,也感受是相對優雅的一種:
import java.util.concurrent.CountDownLatch; public class CountDownLatchDemo extends AbstractBaseDemo { private final CountDownLatch countDownLatch = new CountDownLatch(1); @Override public void callback(long response) { System.out.println(Thread.currentThread().getName() + "獲得結果"); System.out.println(response); System.out.println(Thread.currentThread().getName() + "調用結束"); countDownLatch.countDown(); } public static void main(String[] args) { CountDownLatchDemo demo = new CountDownLatchDemo(); demo.call(); try { demo.countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "主線程內容"); } }
CyclicBarrier的狀況和CountDownLatch有些相似:
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class CyclicBarrierDemo extends AbstractBaseDemo { private CyclicBarrier cyclicBarrier = new CyclicBarrier(2); @Override public void callback(long response) { System.out.println(Thread.currentThread().getName() + "獲得結果"); System.out.println(response); System.out.println(Thread.currentThread().getName() + "調用結束"); try { cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } public static void main(String[] args) { CyclicBarrierDemo demo = new CyclicBarrierDemo(); demo.call(); try { demo.cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "主線程內容"); } }
綜上,就是本次須要說的幾種方法了。事實上,全部的方法都是同一個原理,也就是在調用的線程中進行阻塞等待結果,而在回調中函數中進行阻塞狀態的解除。