• 线程2---异步1


      在Java中什么是同步?什么是异步?对于这两个概念我们必须要明确。只有明确这两个概念,才会在明确在什么场景下使用同步以及异步。

      在这里我可以形象的举个例子来辨明这两个概念:

      1.同步与异步
      同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
      所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由*调用者*主动等待这个*调用*的结果。
      而异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。
      典型的异步编程模型举个通俗的例子:你打电话问书店老板有没有《分布式系统》这本书,
      如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
      而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。
      在这里老板通过“回电”这种方式来回调。
      2. 阻塞与非阻塞阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。还是上面的例子,
      你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
      在开始解决上述问题之前我们来讨论下使用Callbale接口来创建线程。
    @FunctionalInterface
    public interface Callable<V> {
        /**
         * 返回一个任务的结果,或者抛出一个异常(如果不能计算结果)
         */
        V call() throws Exception;
    }

    Callable接口是一个函数式接口,其中call()方法的返回值的类型就是Callable接口的泛型的类型。但是Callable接口怎么和线程扯上关系呢?  FutureTask类存在一个构造器:如下所示:

    FutureTask(Callable<V> callable) 

    其中的参数正式Callable接口对象;FutureTask类又是实现接口RunnableFuture接口:

    public class FutureTask<V> implements RunnableFuture<V>

    接着看:

    public interface RunnableFuture<V> extends Runnable, Future<V> {
        
        void run();
    }

    最终的还是继承了Runnable接口和Future接口,为了说明关系,我们画出类图:

    package com._thread;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class CallableThread implements Callable<String> {
        /**
         * 票的张数为50张,总共100个人买票
         */
        private int ticket = 50;// 表示票的张数
    
        @Override
        public String call() throws Exception {
            for (int i = 0; i < 100; i++) {
                if (ticket > 0) {
                    System.out.println("买票:ticket = " + this.ticket--);
                }
            }
            return "票卖光了!";
        }
    
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            // 创建Callable接口对象  在这里我创建了两个任务对象
            Callable<String> callable1 = new CallableThread();
            Callable<String> callable2 = new CallableThread();
            // 将创建的callable任务对象存储在FutureTask对象中
            FutureTask<String> task1=new FutureTask<String>(callable1);
            FutureTask<String> task2=new FutureTask<String>(callable2);
            // 启动线程执行任务
            new Thread(task1).start();
            new Thread(task2).start();
            // 上述代码只是执行线程,callable接口是可以产生结果的,futuretask可以获取结果
            // 调用get()方法可以阻止当前执行的线程获取结果
            System.out.println("线程A的返回结果是:"+task1.get());
            System.out.println("线程B的返回结果是:"+task2.get());
    
        }
    }

    这是使用callable接口来创建线程的一种实现过程;好了现在让我们讨论Java异步编程吧!

    上面的图可以说明我们的一个买书的过程,在Java中可以视这个操作为同步操作:

    package com._thread;
    
    public class SlowWorker {
        public SlowWorker() {}
    
        public void doWork() {
            try {
                System.out.println("==== 找书, 找书, 找书 ====== ");
                Thread.sleep(2000);
                System.out.println("==== OK! ======");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            SlowWorker worker = new SlowWorker();
            // 主线程模拟买书的人,doWork()方法模拟书店老板的行为!
            System.out.println("老板,我要买书" + new java.util.Date());
            worker.doWork();
            System.out.println("... 老板在找书的过程中我可以干些事情吗!....");
    
            System.out.println("书买到了" + new java.util.Date());
            System.exit(0);
        }
    }

    看运行结果:

    老板,我要买书Sun Jan 21 01:49:35 CST 2018
    ==== 找书, 找书, 找书 ====== 
    ==== OK! ======
    ... 老板在找书的过程中我可以干些事情吗!....
    书买到了Sun Jan 21 01:49:37 CST 2018

    以上的操作确实是一种同步的操作;主线程运行开始后,调用doWork()方法,而doWork()方法需要休眠2s.但是主线程没有继续执行,而是等待了,大家是不是感觉这样做事很没有效率。换言之,如果你在书店买书,老板找一天,你会持续等待下去吗?

    接下来我们谈谈ExecutorService这个接口,它可以表示线程池对象;当线程空闲时,它可以接受一个提交给ExecutorService的callable对象,当线程结束的时候,他会返回一个Future.

    package com._thread;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    /**
     * 异步编程
     * @author gosaint
     *
     */
    public class AsynchronousWorker {
    
        public AsynchronousWorker() {}
        
        public void doWork() {
            try {
                System.out.println("==== 找书, 找书, 找书 ====== ");
                Thread.sleep(2000);
                System.out.println("==== OK! ======");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public static void main(String[] args) {
            
            SlowWorker worker = new SlowWorker();
            // 主线程模拟买书的人,doWork()方法模拟书店老板的行为!
            System.out.println("老板,我要买书" + new java.util.Date());
            // 此时我们创建一个线程池,个数为3
            ExecutorService service = Executors.newFixedThreadPool(3);
            // 此时存在一个线程池对象,线程池对象提交任务,是一个callable接口
            Future<String> future = service.submit(new Callable<String>() {
    
                @Override
                public String call() throws Exception {
                    new AsynchronousWorker().doWork(); 
                    return null;
                }
            });
            System.out.println("... 老板在找书的过程中我可以干些事情吗!....");
            System.out.println("做爱做的事情!");
            
             try {
                     //调用此方法可以获取我们任务的执行结果,但是会阻止主线程的运行,直到得到一个结果
                    future.get(); 
             } catch (InterruptedException e) {
                 e.printStackTrace();
             } catch (ExecutionException e) {
                 e.printStackTrace();
             }
            System.out.println("书买到了" + new java.util.Date());
            System.exit(0);
            
        }
        
        
    
    }

    运行结果如下:

    老板,我要买书Sun Jan 21 02:16:13 CST 2018
    ... 老板在找书的过程中我可以干些事情吗!....
    做爱做的事情!
    ==== 找书, 找书, 找书 ====== 
    ==== OK! ======
    书买到了Sun Jan 21 02:16:15 CST 2018
    

     主线程开始运行,接着我们向ExecutorService提交了一个买书的任务,之后我们在干其他的事情。而最后呢,Future对象从ExecutorService获取到了执行的结果。我们调用get()方法获取到了执行的结果;倘若我们没有这个调用,那么还是一个并行计算,那么老板找书的结果不会立马给我们返回的; 

  • 相关阅读:
    爬虫简介
    MongoDb安装pymongo和mongoengine使用
    简单使用WebSocket实现聊天室
    DBUtils
    FLASK 的Session和MoudelForm插件
    第十一篇 CBV和闪现
    HDOJ 4699 Editor 对顶栈
    [NOI1999]内存分配
    横截面图
    STL List Set
  • 原文地址:https://www.cnblogs.com/gosaint/p/8320866.html
Copyright © 2020-2023  润新知