并发系列——异步

一、概念

<b>同步</b>:【我调用的,必须等待结果】去肯德基吃饭。点餐后一直等待套餐备齐,我才端着套餐去找位置。(我需要等待服务员把餐备齐了才可以。同步通常只有一个线程。这里的这个线程就是“我”)

<b>异步</b>:【我调用的,不需要等待结果,就返回了】去牛排店吃饭。点餐后我就拿票据去位置上做其他事情,过一会儿服务员根据票据(副票)把餐送到我桌子上。(通常有两个线程:一个线程是"我",另一个线程是“服务员”。还有一个状态“票据”,来进行回调)

<b>阻塞</b>:【我调用的,由于某些问题阻塞住自己,不做其他事情,就盯着看这个问题】去肯德基吃饭。点餐后,服务员告诉你没这个套餐了,但我还是死活一直等待着套餐备齐,而且就占着点餐的坑位,直到有这个套餐为止。可能今天没有,明天就有了呢?或者下个月就有了呢?反正一直等,等到有该套餐为止。(是一个线程把自己挂起了,就是牛角尖碰上铁公鸡)

<b>非阻塞</b>:【我调用的,出了某些问题,我时不时过来看一下问题解决没,而不用一直盯着看】去肯德基吃饭。点餐后,服务员告诉你没这个套餐了,但我还是想吃。那怎么办?我时不时的过来问一下有没有。不会傻傻的一直在那边等待食物的到来。

二、示例

1. 异步+同步+阻塞

主线程A发送一个任务后就直接返回了,利用<b>Future</b>由另一个线程B去处理任务。但一旦主线程调用<b>future.get() </b>等待B线程的完成结果时候,而这一步进入阻塞,<b>doSomething</b>方法需要等阻塞返回后才能执行。

1、主线程调用一个任务,由另一个线程B去处理,立马返回。主线程可以继续干其他事情。(异步)
2、 当主线程调用future.get()方法,他需要阻塞等待线程B的计算结果(同步+阻塞)。

package com.tinygao.thread.asyn;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

import com.google.common.base.Stopwatch;

/**
 * @author tinygao
 * @see {CompletableFutureTest}
 * 两个任务:
 * 1、上传文件
 * 2、计算文本行数
 * 3、任务1和2都完成后返回,上传状态和文本数
 *
 */
@Slf4j
public class FutureTest {

    public static boolean uploadFile() {
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return true;
    }
    
    public static long computeTextLines() {
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 1000_000L;
    }
    
    public static void doSomething() {
        log.info("Do other thing start....");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("Do other thing end , waste 5 s....");
    }
    
    public static void main(String[] args) throws InterruptedException {
        log.info("Start jobs .......");
        boolean uploadFileResult = false;
        Stopwatch st = Stopwatch.createStarted();
        ExecutorService es = Executors.newCachedThreadPool();
        
        /**The first job : Upload file**/
        final Future<Boolean> future = es.submit(FutureTest::uploadFile);
        /**The second job : Compute textLines num**/
        long textLines = computeTextLines();
        
        /**Block the first job, wait for the result **/
        try {
            uploadFileResult = future.get();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        
        /**Notify user the result**/
        log.info("TextLine num is : {}, Upload file {} , waste : {} s", 
                  textLines, 
                  uploadFileResult?"success":"fail",
                  st.elapsed(TimeUnit.SECONDS));
        es.shutdown();
        /**after the first job return **/
        doSomething();
        log.info("End jobs ....... waste total : {} s", st.elapsed(TimeUnit.SECONDS));
        st.stop();
    }
}

2 、异步+非阻塞+事件驱动

主线程A发送一个任务后就直接返回了,利用<b>CompletableFuture</b>由另一个线程B去处理任务。主线程A<b>doSomething</b>不用阻塞等待线程B的任务返回。线程B通过回调的方式告诉主线程“餐到了”。

1、主线程不用等待任务执行结果返回,它可以继续做其他任务。由另一个线程B去处理任务并返回告知(异步)
2、 主线程不用阻塞等待另一个线程的结果(非阻塞)
3 、另一个线程B回调告诉主线程结果来了(异步+事件驱动)

package com.tinygao.thread.asyn;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

import com.google.common.base.Stopwatch;

/**
 * @author tinygao
 * @see {FutureTest}
 * 两个任务:
 * 1、上传文件
 * 2、计算文本行数
 * 3、任务1和2都完成后返回,上传状态和文本数
 */
@Slf4j
public class CompletableFutureTest {
    public static boolean uploadFile() {
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return true;
    }
    
    public static long computeTextLines() {
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 1000_000L;
    }
    
    public static void doSomething() {
        log.info("Do other thing start....");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("Do other thing end , waste 5 s....");
    }
    
    public static void main(String[] args) throws InterruptedException {
        log.info("Start jobs .......");
        Stopwatch st = Stopwatch.createStarted();
        
        /**The first job : Upload file**/
        final CompletableFuture<Boolean> upload = CompletableFuture
                .supplyAsync(CompletableFutureTest::uploadFile);
        
        /**The second job : Compute textLines num**/
        final CompletableFuture<Long> compute = CompletableFuture
                .supplyAsync(CompletableFutureTest::computeTextLines);
        
        compute.thenCombine(upload, (computeResult, uploadResult)-> {
            /**Notify user the result**/
            log.info("TextLine num is : {}, Upload file {} , waste : {} s", 
                      computeResult, 
                      uploadResult?"success":"fail",
                      st.elapsed(TimeUnit.SECONDS));
            return null;
        });

        /**after the first job return **/
        doSomething();
    }
}

另外附上guava示例:

package com.tinygao.thread.asyn;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

import com.google.common.base.Stopwatch;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

/**
 * @author tinygao
 * @see {FutureTest}/{CompletableFutureTest}
 * 两个任务:
 * 1、上传文件
 * 2、计算文本行数
 * 3、任务1和2都完成后返回:上传状态和文本数
 */
@Slf4j
public class GuavaFuture {
    public static boolean uploadFile() {
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return true;
    }
    
    public static long computeTextLines() {
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 1000_000L;
    }
    
    public static void doSomething() {
        log.info("Do other thing start....");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("Do other thing end , waste 5 s....");
    }
    public static void main(String[] args) throws InterruptedException {
        log.info("Start jobs .......");
        Stopwatch st = Stopwatch.createStarted();
        final ListeningExecutorService executor = MoreExecutors.listeningDecorator(
                                                     Executors.newCachedThreadPool());
        
        ListenableFuture<Boolean> uploadResult = executor.submit(CompletableFutureTest::uploadFile);
        ListenableFuture<Long> computeResult = executor.submit(CompletableFutureTest::computeTextLines);
        
        /**第一种方法**/
        /*Futures.whenAllComplete(uploadResult, computeResult).call(()->{
            log.info("TextLine num is : {}, Upload file {} , waste : {} s", 
                      computeResult.get(), 
                      uploadResult.get()?"success":"fail",
                      st.elapsed(TimeUnit.SECONDS));
            executor.shutdown();
            return null;
        });*/
        
        /*******************************************************************
         * ******************黄金分割线****************************************
         * ****************************************************************/
        /**第二种方法**/
        Futures.addCallback(uploadResult, new FutureCallback<Boolean>() {

            @Override
            public void onSuccess(Boolean result) {
                log.info("UploadResult do success, waste {} s", st.elapsed(TimeUnit.SECONDS));
            }

            @Override
            public void onFailure(Throwable t) {
                log.info("throw exception ", t);
                
            }
        });
        
        Futures.addCallback(computeResult, new FutureCallback<Long>() {

            @Override
            public void onSuccess(Long result) {
                log.info("ComputeResult do success, waste {} s", st.elapsed(TimeUnit.SECONDS));
            }

            @Override
            public void onFailure(Throwable t) {
                log.info("throw exception ", t);
            }
        });

        /**after the first job return **/
        doSomething();  
        executor.shutdown();
    }
}

三、总结

<b>1、同步异步与阻塞与否无直接关系。
2、同步和异步只关心调用后的结果有没有返回。
3、阻塞和非阻塞只关心调用后等待结果的时候,线程的状态如何。

  • 启用future起处理另一个任务,不用等待返回任务结果,这就是异步。——【看的是结果】
  • 调用future.get的时候,主线程被阻塞住了,导致doSomething无法执行。线程状态是被卡住了,这就是阻塞。——【看的是线程状态】
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容

  • 一、并发 进程:每个进程都拥有自己的一套变量 线程:线程之间共享数据 1.线程 Java中为多线程任务提供了很多的...
    SeanMa阅读 2,379评论 0 11
  • layout: posttitle: 《Java并发编程的艺术》笔记categories: Javaexcerpt...
    xiaogmail阅读 5,770评论 1 19
  • 接着上节 condition_varible ,本节主要介绍future的内容,练习代码地址。本文参考http:/...
    jorion阅读 14,708评论 1 5
  • 歌曲的特定记忆 现在再听阿肆等民谣歌曲 脑子里出现的是在去哪儿网实习的时候 每天上下班从家门口走到地铁口的那段路。
    圆柱体阅读 104评论 0 0
  • ClrsDream阅读 313评论 0 0