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

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

  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进行了增强,只需要指定任务执行结束或异常时的回调对象,在结束后会自动执行,并且支持任务的串行,并行和多个任务都执行完毕后再执行等高级方法。
中外科学家研究发现三亿年前发生一次巨量碳排放事件导致海洋缺氧和生物多样性下降本报南京5月3日电(记者苏雁通讯员姬尊雨)5月2日,中国科学院南京地质古生物研究所与南京大学美国加州大学戴维斯分校等在国际著名的科学期刊美国科学院院报(PNAS)上合作发表一项重要42岁,老家县城公务员,工资到手4000,想辞职去干快递,希望大家给点意见,如何规划?当你转一圈后,回头再看看,再想想,还是体内最好。需无大富大贵,但稳定且压力较小。你这基本是故意制造舆情,像你这样出勤和对待上司的态度,如果是在体制外,想能达到四千以上的税后和缴纳社特斯拉准备在中国建第二工厂,面对各大城市邀请,提出申请条件特斯拉大家都应该非常清楚,这几年特斯拉在中国消费者和上海的大力支持下,如今特斯拉在汽车行业的市值排第一,创始人马斯克也因此成为世界首富。马斯克如今特斯拉汽车大火,上海的工厂已经不能4月主要商业网站平台受理侮辱谩骂等举报43。96万件业内惩治网络暴力平台应提供一键取证央广网北京5月19日消息(记者黄昂瑾)5月18日,国家互联网信息办公室发布的数据显示,2022年4月,全国各级网络举报部门受理举报1602。9万件,环比增长27。4同比增长7。2。我给父亲买了1000多的智能手机,性价比非常不错我想给父亲买一款智能手机,性价比要高,后来在网上买了一款二手的手机,不过这款手机成色很新,我觉得非常可以,这款手机就是小米10。首先就是手机的处理器,是高通骁龙865,骁龙865没美国人最喜欢哪款手机?iPhone只排第三,苹果表现令人失望美国媒体公布一份客户满意度指数报告,针对23000多名美国用户展开调查,共评选出消费者最喜欢的15款手机,结果令人大感意外。作为全球单品销量最高又是美国本土品牌的苹果竟然只排第三,京东物流连亏,刘强东把钱花在兄弟们身上文乐居财经杨宏彬去年5月28日,京东物流正式登陆港交所,发行价40。36港元,首日高开14。1,报46。05港元,市值2800亿港元。自此,京东物流也成为继中通快递之后,国内第二家iPhone14日子定了!北京时间9月14日与用户见面中关村在线消息近日,外媒iDropNews曝光了iPhone14系列的发布会日期美国时间9月13日,即北京时间9月14日正式发布。其也放出了iPhone14Pro的宣传图,可以看到520最多满减一千!14k价位性价比最高空净推荐,附上优惠时间表最近520618各项大促来袭,现在正是买家电最合适的时候!今天应后台朋友留言,分享1k4k价位中性价比最高的空净给大家!全都是自己买来用过一段时间再推荐的,大家可以按照自己的需求和vivoS15今晚发布天玑8100索尼IMX766V你心动吗?手机中国新闻众所周知,vivoS系列的人群定位是年轻人,自信不止是这个系列手机一贯的产品精神。5月19日晚上7点,vivo将召开新机发布会,并将正式发布其新机vivoS15系列。据结束野蛮生长,新能源充电桩市场竞争开启下半场2018年中央首次提出新基建概念后,新能源充电桩作为七大方向内唯一一个覆盖大众需求的领域,不断吸引新玩家进入。随着新能源车的增多,充电市场的供需矛盾问题突出,也引起了政府部门的重视
手机店老板建议,目前这4部智能手机值得考虑,几乎没有短板不知道买什么手机,那肯定要去问那些懂手机的人,去线下实体店问那些手机老板,相信你一定可以买到一个满意的手机。今天周日,没什么事就跑去逛街了,走进了一家苹果手机店瞧了瞧,顺便了问了这滴滴下架了,美团打车换logo,现在是中国网约车最好的春天?7月9日,网约车平台滴滴,因个人信息安全问题,被勒令下架各平台。新用户无法下载和注册滴滴出行。美团打车是2017年2月上架的打车业务,并且在2019年4月,将打车功能合并入美团AP高温现象对小米的影响有限,无论是商业视角,还是客户体感视角科技让生活更有品首先,我相信,小米在测试阶段,一定测试过手机温度的表现。我也相信,小米一定考虑过主流游戏玩家用小米11的真实表现。因此,温度过高这种现象,小米一定在手机发布之前是知高通已经恢复供货华为,那台积电还有机会恢复代工吗?了解更多科技资讯尽在圈聊科技。今天跟大家聊一聊高通已经恢复供货华为,台积电有机会恢复代工吗?华为近些年在科技领域取得了众多的成就,在很多技术上甚至超过了国际顶尖水平,就连很多美企也开箱测试两千八一套的四合一家庭K歌一体机家庭K歌系统对于你来说并不陌生,为了让使用者更加便利往往会把设备合并起来,以易于操作,节省成本为目的。四合一K歌一体机三合一K歌一体机话筒效果器功放(8寸或10寸音箱)三合一的一体苹果iPhone12Pro新广告宣传夜间拍摄能力IT之家7月11日消息7月10日,苹果公司发布了一段全新的iPhone12Pro的广告视频,重点展现了夜间拍摄能力。整部短片时长38秒,场景在漆黑的车库以及室内。短片配乐为HipHiPhone13变相降价,十三香或成真文机sir相信有持续关注机sir至今的果粉们应该都了解了,和往年一样,在苹果秋季发布会之前,产业链就不断出现了有关新款iPhone的消息,每一代的苹果产品都会行业有巨大的推进作用,为什么很多人都愿意买4G版的iPhone11,却不情愿买4G版的Mate40?虽然5G时代即将来临,但4G手机并未退出历史的舞台。比如2019年发布的iPhone11,就是一款很典型的4G手机,再比如4G版本的华为Mate40,同样受到很多人的欢迎。但如果把神州租车止于七年之痒,比嘀嘀滴答出行还可恶出行市场盈利的难度非常大。这或将意味着,诸多出行公司还将在这汪潮水中继续挣扎,寻找新的方向。首汽曹操等网约车平台,滴答哈啰一喂出行等顺风车平台相互追逐,使得出行领域快速发展,打造更小米CC11Pro曝光高通骁龙870芯片,11。3x英寸大底镜头IT之家7月11日消息此前小米CC产品经理品牌发言人魏思琪在回复网友时明确表示,小米新一代CC系列机型将于今年第三季度发布,证实小米CC系列依然存在且其新机离面世已经很近了。此外,换宽带了,可能还是会用移动,因为资费优惠力度大最近在忙着把家具什么时候的放到新家里去(昨天从下午两点多一直呆到了晚上八点,就为了进餐厅桌椅和书柜书桌立柜),考虑要不要把6月停掉的宽带换到新家去,但是又有些纠结。除了爸妈过来的时