美文网首页
并发系列——异步

并发系列——异步

作者: hello高world | 来源:发表于2017-02-04 16:02 被阅读0次

    一、概念

    <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无法执行。线程状态是被卡住了,这就是阻塞。——【看的是线程状态】

    相关文章

      网友评论

          本文标题:并发系列——异步

          本文链接:https://www.haomeiwen.com/subject/uunjittx.html