范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

并发编程之异步调用获取返回值

  Runnable
  在创建线程时,可以通过new Thread(Runnable)方式,将任务代码封装在Runnable的run()方法中,将Runnable作为任务提交给Thread,或者使用线程池的execute(Runnable)方法处理。 public class RunnableDemo {     public static void main(String[] args) {         ExecutorService executorService = Executors.newCachedThreadPool();         executorService.submit(new MyRunnable());     } }  class MyRunnable implements Runnable {     @Override     public void run() {         System.out.println("runnable正在执行");     } }Runnable的问题
  如果你之前有看过或者写过Runnable相关的代码,肯定会看到有说Runnable不能获取任务执行结果的说法,这就是Runnable存在的问题,那么可不可以改造一下来满足使用Runnable并获取到任务的执行结果呢?答案是可以的,但是会比较麻烦。
  首先我们不能修改run()方法让它有返回值,这违背了接口实现的原则;我们可以通过如下三步完成: 我们可以在自定义的Runnable中定义变量,存储计算结果; 对外提供方法,让外部可以通过方法获取到结果; 在任务执行结束之前如果外部要获取结果,则进行阻塞;
  如果你有看过我之前的文章,相信要做到功能并不复杂,具体实现可以看我下面的代码。 public class RunnableDemo {     public static void main(String[] args) throws ExecutionException, InterruptedException {         MyRunnable myRunnable = new MyRunnable<>();         new Thread(myRunnable).start();         System.out.println(LocalDateTime.now() + " myRunnable启动~");         MyRunnable.Result result = myRunnable.getResult();         System.out.println(LocalDateTime.now() + " " + result.getValue());     } }  class MyRunnable implements Runnable {     // 使用result作为返回值的存储变量,使用volatile修饰防止指令重排     private volatile Result result;      @Override     public void run() {         // 因为在这个过程中会对result进行赋值,保证在赋值时外部线程不能获取,所以加锁         synchronized (this) {             try {                 TimeUnit.SECONDS.sleep(2);                 System.out.println(LocalDateTime.now() + " run方法正在执行");                 result = new Result("这是返回结果");             } catch (InterruptedException e) {                 e.printStackTrace();             } finally {                 // 赋值结束后唤醒等待线程                 this.notifyAll();             }         }     } 	// 方法加锁,只能有一个线程获取     public synchronized Result getResult() throws InterruptedException { 		// 循环校验是否已经给结果赋值         while (result == null) {             // 如果没有赋值则等待             this.wait();         }         return result;     } 	// 使用内部类包装结果而不直接使用T作为返回结果     // 可以支持返回值等于null的情况     static class Result {         T value;         public Result(T value) {             this.value = value;         }         public T getValue() {             return value;         }     } }
  从运行结果我们可以看出,确实能够在主线程中获取到Runnable的返回结果。
  以上代码看似从功能上可以满足了我们的要求,但是存在很多并发情况的问题,实际开发中极不建议使用。在我们实际的工作场景中这样的情况非常多,我们不能每次都这样自定义搞一套,并且很容易出错,造成线程安全问题,那么在JDK中已经给我们提供了专门的API来满足我们的要求,它就是 Callable 。 Callable
  我们通过Callable来完成我们上面说的1-1亿的累加功能。 public class CallableDemo {     public static void main(String[] args) throws ExecutionException, InterruptedException {         Long max = 100_000_000L;         Long avgCount = max % 3 == 0 ? max / 3 : max / 3 + 1;         // 在FutureTask中存放结果         List> tasks = new ArrayList<>();         for (int i = 0; i < 3; i++) {             Long begin = 1 + avgCount * i;             Long end = 1 + avgCount * (i + 1);             if (end > max) {                 end = max;             }             FutureTask task = new FutureTask<>(new MyCallable(begin, end));             tasks.add(task);             new Thread(task).start();         }                  for (FutureTask task : tasks) {             // 从task中获取任务处理结果             System.out.println(task.get());         }     } } class MyCallable implements Callable {     private final Long min;     private final Long max;     public MyCallable(Long min, Long max) {         this.min = min;         this.max = max;     }     @Override     public Long call() {         System.out.println("min:" + min + ",max:" + max);         Long sum = 0L;         for (Long i = min; i < max; i++) {             sum = sum + i;         }         // 可以返回计算结果         return sum;     } }
  运行结果:
  可以在创建线程时将Callable对象封装在FutureTask对象中,交给Thread对象执行。
  FutureTask之所以可以作为Thread创建的参数,是因为FutureTask是Runnable接口的一个实现类。
  既然FutureTask也是Runnable接口的实现类,那一定也有run()方法,我们来通过源码看一下是怎么做到有返回值的。
  首先在FutureTask中有如下这些信息。 public class FutureTask implements RunnableFuture {     // 任务的状态     private volatile int state;     private static final int NEW          = 0;     private static final int COMPLETING   = 1;     private static final int NORMAL       = 2;     private static final int EXCEPTIONAL  = 3;     private static final int CANCELLED    = 4;     private static final int INTERRUPTING = 5;     private static final int INTERRUPTED  = 6;      // 具体任务对象     private Callable callable;     // 任务返回结果或者异常时返回的异常对象     private Object outcome;      // 当前正在运行的线程     private volatile Thread runner; 	//      private volatile WaitNode waiters;     private static final sun.misc.Unsafe UNSAFE;     private static final long stateOffset;     private static final long runnerOffset;     private static final long waitersOffset; }public void run() {     // 任务状态的校验     if (state != NEW ||         !UNSAFE.compareAndSwapObject(this, runnerOffset,                                      null, Thread.currentThread()))         return;     try {         Callable c = callable;         if (c != null && state == NEW) {             V result;             boolean ran;             try {                 // 执行callable的call方法获取结果                 result = c.call();                 ran = true;             } catch (Throwable ex) {                 result = null;                 ran = false;                 // 有异常则设置返回值为ex                 setException(ex);             }             // 执行过程没有异常则将结果set             if (ran)                 set(result);         }     } finally {         runner = null;         int s = state;         if (s >= INTERRUPTING)             handlePossibleCancellationInterrupt(s);     } }
  在这个方法中的核心逻辑就是执行callable的call()方法,将结果赋值,如果有异常则封装异常。
  然后我们看一下get方法如何获取结果的。 public V get() throws InterruptedException, ExecutionException {     int s = state;     if (s <= COMPLETING)         // 这里会阻塞等待         s = awaitDone(false, 0L);     // 返回结果     return report(s); } private V report(int s) throws ExecutionException {     Object x = outcome;     if (s == NORMAL)         return (V)x;     if (s >= CANCELLED)         // 状态异常情况会抛出异常         throw new CancellationException();     throw new ExecutionException((Throwable)x); }
  在FutureTask中除了get()方法还提供有一些其他方法。 get(timeout,unit):获取结果,但只等待指定的时间; cancel(boolean mayInterruptIfRunning):取消当前任务; isDone():判断任务是否已完成。 CompletableFuture
  在使用FutureTask来完成异步任务,通过get()方法获取结果时,会让获取结果的线程进入阻塞等待,这种方式并不是最理想的状态。
  在JDK8中引入了CompletableFuture,对Future进行了改进,可以在定义CompletableFuture传入回调对象,任务在完成或者异常时,自动回调。 public class CompletableFutureDemo {     public static void main(String[] args) throws InterruptedException {         // 创建CompletableFuture时传入Supplier对象         CompletableFuture future = CompletableFuture.supplyAsync(new MySupplier());         //执行成功时         future.thenAccept(new MyConsumer());         // 执行异常时         future.exceptionally(new MyFunction());         // 主任务可以继续处理,不用等任务执行完毕         System.out.println("主线程继续执行");         Thread.sleep(5000);         System.out.println("主线程执行结束");     } }  class MySupplier implements Supplier {     @Override     public Integer get() {         try {             // 任务睡眠3s             TimeUnit.SECONDS.sleep(3);         } catch (InterruptedException e) {             e.printStackTrace();         }         return 3 + 2;     } } // 任务执行完成时回调Consumer对象 class MyConsumer implements Consumer {     @Override     public void accept(Integer integer) {         System.out.println("执行结果" + integer);     } } // 任务执行异常时回调Function对象 class MyFunction implements Function {     @Override     public Integer apply(Throwable type) {         System.out.println("执行异常" + type);         return 0;     } }
  以上代码可以通过lambda表达式进行简化。 public class CompletableFutureDemo {     public static void main(String[] args) throws InterruptedException {         CompletableFuture future = CompletableFuture.supplyAsync(() -> {             try {                 // 任务睡眠3s                 TimeUnit.SECONDS.sleep(3);             } catch (InterruptedException e) {                 e.printStackTrace();             }             return 3 + 2;         });         //执行成功时         future.thenAccept((x) -> {             System.out.println("执行结果" + x);         });         future.exceptionally((type) -> {             System.out.println("执行异常" + type);             return 0;         });         System.out.println("主线程继续执行");         Thread.sleep(5000);         System.out.println("主线程执行结束");     } }
  通过示例我们发现CompletableFuture的优点: 异步任务结束时,会自动回调某个对象的方法; 异步任务出错时,会自动回调某个对象的方法; 主线程设置好回调后,不再关心异步任务的执行。
  当然这些优点还不足以体现CompletableFuture的强大,还有更厉害的功能。 串行执行
  多个CompletableFuture可以串行执行,如第一个任务先进行查询,第二个任务再进行更新 public class CompletableFutureDemo {     public static void main(String[] args) throws InterruptedException {         // 第一个任务         CompletableFuture future = CompletableFuture.supplyAsync(() -> 1234);         // 第二个任务         CompletableFuture secondFuture = future.thenApplyAsync((num) -> {             System.out.println("num:" + num);             return num + 100;         });         secondFuture.thenAccept(System.out::println);         System.out.println("主线程继续执行");         Thread.sleep(5000);         System.out.println("主线程执行结束");     } }并行任务
  CompletableFuture除了可以串行,还支持并行处理。 public class CompletableFutureDemo {     public static void main(String[] args) throws InterruptedException {         // 第一个任务         CompletableFuture oneFuture = CompletableFuture.supplyAsync(() -> 1234);         // 第二个任务         CompletableFuture twoFuture = CompletableFuture.supplyAsync(() -> 5678); 		// 通过anyOf将两个任务合并为一个并行任务         CompletableFuture anyFuture = CompletableFuture.anyOf(oneFuture, twoFuture);          anyFuture.thenAccept(System.out::println);         System.out.println("主线程继续执行");         Thread.sleep(5000);         System.out.println("主线程执行结束");     } }
  通过anyOf()可以实现多个任务只有一个成功,CompletableFuture还有一个allOf()方法实现了多个任务必须都成功之后的合并任务。 小结
  Runnable接口实现的异步线程默认不能返回任务运行的结果,当然可以通过改造实现返回,但是复杂度高,不适合进行改造;
  Callable接口配合FutureTask可以满足异步任务结果的返回,但是存在一个问题,主线程在获取不到结果时会阻塞等待;
  CompletableFuture进行了增强,只需要指定任务执行结束或异常时的回调对象,在结束后会自动执行,并且支持任务的串行,并行和多个任务都执行完毕后再执行等高级方法。
拜登表示成功避免圣诞节供应链危机据报道,美国总统拜登表示,在周三与美国官员和包括联邦快递在内的私营企业举行会议之前,美国为消除供应链混乱所采取的行动,已经成功避免了圣诞节前的危机。白宫表示,在推动港口24小时运营英特尔不是道歉,而是又一次挑衅!吃饭砸锅的行为,必须要遏制住据环球网报道,在中国外交部今天(12月23日)举行的例行记者会上,有记者就芯片巨头企业英特尔,今天对自身不当涉疆言论致歉一事,询问中方态度,对此,中国外交部发言人赵立坚回应称我们(给女朋友讲某宝是如何设计用户权限管理的(一)一概述java应用系统设计过程中,用户认证用户授权鉴权是绕不过去的话题。如果这个权限管理的设计,没有做到与业务系统的隔离,拓展性不够强,很容易就会拖后腿。这个问题应该做过开发的同学OK直接K。O用户,无法平仓致用户爆仓6000万!!吃相太难看6000万这个数字对于普通人来说可能是一辈子都达到不了的数字,可是在币圈却是每天都实实在在流通着,这一秒拥有下一秒可能就失去了,相信是谁也无法接受,更不用说这种不公平的规则之下的强还在讨论NFT区块链元宇宙?Web3已晋升年度科技热词或是互联网终极形态科创板日报(上海,编辑宋子乔)讯,当大家还在热议元宇宙NFT区块链的时候,又一个新词已成为焦点。国外媒体近日评出了2021年12大科技热词,Web3榜上有名,特斯拉首席执行官马斯克中印再起矛盾?莫迪接二连三对中企赶尽杀绝,却不知自己多愚蠢据印度商业标准报报道,在12月23日的时候,印度财政部税收局对当地的的中国企业进行了一次重大的打击。整个在印度的手机制造商和分销商都包括在内,其中,有来自中国的手机制造商OPPO小为新能源汽车注入发展正能量来源经济日报完全想不明白,现在大家为什么还买油车,除了能闻点汽油味,别的还有什么好?听个响?蔚来汽车创始人董事长李斌没想到,他在蔚来日NIODAY2021的一句讲话,竟引发了争议。数据结构队列的链式存储和链式存储队列的顺序存储头文件dynamicArray。hpragmaonceincludestdio。hincludestring。hincludestdlib。h动态数组结构体strucMIUI13首批支持,小米骁龙870旗舰迎来降价对于智能手机来说其性能是硬件配置与操作系统软件互相配合协调的结果,有iPhone手机用户表示自己之所以选择iPhone手机是因为iOS系统,可见操作系统的重要性。在安卓系统方面小米不用等新款手机了,这四款旗舰已经开始清仓甩货,比新款要实用随着新款手机的到来,可能这次又要让我们失望了。比如小米12随着这两天参数的不断曝光,我们也可以看到今年他的标配版本将不会采用2K屏幕,也就是说是素质还不如上一代,虽然表面说是为了轻明年机圈这些值得期待的顶级旗舰,神仙大战,华为终于有新机了上篇给大家盘点了2022年最值得期待的七款机型之三,今天也给大家带来剩余四款的预测,看看究竟明年旗舰机之争,哪款更出色,大家也可以看看哪款更喜欢,为明年换手机提前做准备!三星S22
三星Exynos2200GPU处理器性能大涨超越苹果A14芯据外媒消息,三星下一代Exynos移动处理器将集成AMDRDNA2(mRNDA)架构核显,已经曝光的数据来看,其多项跑分测试均超越了苹果A14仿生处理器。三星Exynos2200GE拆解iQOONeo5活力版的高性价比都在哪实现的呢?iQOONeo5活力版虽然名字和iQOONeo5相似,但其实仅有处理器一致。Neo5活力版更像是Neo5的改版,将屏幕升级,在快充独显以及HiFi几处也做了一些增删。整体成了一款2不知不觉又到了一年一度苹果出新机的季节作为一年一度的科技春晚,苹果新品发布会马上就要到来了!虽然新机外观已经基本被曝光得七七八八的了,但还是完全不影响世界各地的数码科技爱好者熬夜观看苹果新品发布会,为什么苹果新品备受业红米手机好用吗?近日,三款型号为M1804C3CCM1804C3CE和M1804C3DE的小米新机获工信部入网许可,从规格上来看应该是红米A6的衍生版本。三款设备均装备分辨率为1440720的5。你会为了面子买苹果手机吗?谢谢邀请,不得不承认,iPhone手机因为其时尚的外观高昂的价格在很多人心中成为身份尊贵的象征,认为选择iPhone手机就是有面子,但乐优优认为用苹果手机可能会让你觉得有面子,不代如何在享受大数据和人工智能便利的同时,兼顾安全可靠可控?来源方圆(图片来源摄图网)网约车与出租车哪个更便宜?苹果手机与非苹果手机,谁打车更贵?新客户打车便宜还是老客户打车便宜?带着这样的疑问,复旦大学教授孙金云带领他的学生,一个月时间内苹果官翻iMac上架比全新机便宜1700超划算前段时间,苹果在北美和欧洲相继上架官翻版iMac。果不其然,近日苹果中国官网也正式上架了官翻版iMac。以M1处理器24英寸版本iMac为例,其售价为8499元,比全新机型便宜17微信将推出聊天记录云备份服务,180元年,然而很多系统早已支持据报道显示腾讯拟推出个人微信云存储服务,可以实现微信聊天记录云备份恢复,计费标准苹果180元年左右,安卓130元年左右。首先这项功能是好功能,目前微信聊天记录备份只有手动备份,当手AAX三个问题揭开数字货币交易所的面纱数字货币令不少投资者成为千万甚至是亿万富豪,这一点都不夸张。但是今天要讲的是数字货币的另外一个重要概念,并且在数字货币领域起到一个非常重要的作用的数字货币交易所,或者可以叫数字货币成都车展电动版高尔夫亮相,双色车身续航430KM,能火吗?早些年的国内SUV市场潜力逐渐被激发时,大众的重心依旧停留在轿车市场,一直到了2018年才推出10多辆不同定位的SUV,这也就意味顶着最了解国人需求名号的大众,错过了SUV最佳起步老马现如今的状况20小时前马云作为国内互联网行业的领军人物,其创建的阿里巴巴已经成为国内最大的互联网企业之一。阿里掌握丰厚的市场资源,马云也功成身退。可是马云始料未及,自己退休之后阿里会发生这么多