深入学习JDK 线程池(之七)
来源:互联网 发布:明天教室网络课怎么样 编辑:程序博客网 时间:2024/05/01 09:49
一、Submit流程
前面几章节已经简单提及类AbstractExecutorService中的submit方法,现在我们来学习一下submit方法的设计及内部实现。
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Object> ftask = newTaskFor(task, null); execute(ftask); return ftask; } public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask; } public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }
之前通过JDK API的介绍可知,submit主要是用来提交执行Callable线程对象,其实submit的重载方法也可以处理Runnable对象。
由上面方法可知,Runnable和Callable对象都作为参数得到一个RunnableFuture接口实例对象(实例化的类为FutureTask),其中需要用到RunnableAdapter适配器类,将Runnable适配成Callable,因为FutureTask只接受Callable对象。如下面构造器代码:
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); sync = new Sync(callable); }
public FutureTask(Runnable runnable, V result) { sync = new Sync(Executors.callable(runnable, result)); }
从构造器代码可知:内部类Sync只接受Callable对象,对Callable对象的处理实际上是交由Sync内部类。FutureTask的关键方法run()和get(),都是由Sync类的innerRun()和innerGet()来实现的,如代码所示:
void innerRun() { if (!compareAndSetState(0, RUNNING)) return; try { runner = Thread.currentThread(); if (getState() == RUNNING) // recheck after setting thread innerSet(callable.call()); else releaseShared(0); // cancel } catch (Throwable ex) { innerSetException(ex); } }
V innerGet() throws InterruptedException, ExecutionException { acquireSharedInterruptibly(0); if (getState() == CANCELLED) throw new CancellationException(); if (exception != null) throw new ExecutionException(exception); return result; }
阻塞式等待机制实现
我们之前学习到,Future接口的get方法的执行具有阻塞性,它的实现原理就在innerGet()方法里,实现的过程有点复杂,我只是简单地讲解一下思路。acquireSharedInterruptibly(0)方法调用时,会调用FutureTask类的tryAcquireShared方法,该方法的一个作用是判断线程对象有没有执行完,若未执行完,会跳到AbstractQueuedSynchronizer类中的doAcquireSharedInterruptibly方法(中间的方法调用过程可查阅源码),该方法如下:
/** * Acquires in shared interruptible mode. * @param arg the acquire argument */ private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) break; } } catch (RuntimeException ex) { cancelAcquire(node); throw ex; } // Arrive here only if interrupted cancelAcquire(node); throw new InterruptedException(); }
看源码可知,在方法的for循环里会不停地判断该线程任务有没有执行完,直到线程任务执行完毕,才能跳出该方法,大概就是通过这种思路去实现阻塞式等待功能的。
二、线程池Submit(Callable)应用场景
线程池对象调用Submit(Callable)方法后,能返回一个Future对象,调用Future对象的get方法,可以阻塞式等call()方法执行完返回结果,现有两种使用场景:
场景一:所有的Callable对象都submit后,用集合收集返回的Future对象,再遍历该集合,逐个调用get()方法,这样可以使多个callable线程并发执行,第一个调用get()方法的Future对象是最早提交的,等待的时间理论上会比较短,但如果callable比较多,会导致大量的线程进入阻塞状态。
场景二:sumbit(callable)后,立即调用Future.get()方法,由于get()方法是阻塞式运行的,相当于没有并发运行任务。如果需要并发执行的效果,可以将submit这一环节设计成并发的,即在多线程对象里调用submit方法。可能看到这儿各位会不理解为什么要这样做,这样做唯一的目的就是防止大量的线程启动。注意是通过阻塞等待防止线程启动,跟线程池通过最大执行的线程控制还是有区别的,并且在有些需求环境下需要考虑用这种方式。
写到这里,线程池这一部分的学习算告一段落,回想回想其实仍然有非常多的地方没有弄明白,估计是掌握的东西不够,有些东西只能揣摩,无法从本质上去理解。继续努力吧
- 深入学习JDK 线程池(之七)
- 深入学习JDK 线程池(之二)
- 深入学习JDK 线程池(之三)
- 深入学习JDK 线程池(之四)
- 深入学习JDK 线程池(之五)
- 深入学习JDK 线程池(之六)
- 深入学习JDK 线程池(之一)
- JDK源码走读之深入理解线程池(ThreadPoolExecutor)
- ICE学习(七)-线程池
- Android学习系列之(七)Fragment深入
- jdk源码学习:线程池
- C++ STL学习之七:配接器priority_queue深入学习
- 深入学习keepalived之预备工作--线程
- 深入Java之线程池
- Redis深入之路(七)
- 框架学习七-2:ViewPager+Fragment之Fragment深入介绍
- 深入学习Spring框架之七SpEL表达式装配
- java深入学习七之封装与继承,多态
- HDU-OJ-1698 Just a Hook
- 母猪的产后护理
- CAN总线负载率原理及计算
- HDU 1896 Stones ——STL 优先队列
- 数据库同步-重置同步数据
- 深入学习JDK 线程池(之七)
- 寄存器编址
- c++-结构体内存对齐
- Cocos2d学习之路五(Box2d使用CCPhysicsSprite时编译不通过解决方法)
- ubuntu12.04-64 server系统上hadoop2.2.0/2.3.0环境搭建及总结
- 定义数组为有序数组模型,并利用二分法进行查找,删除特定值(缺点:只能删除一个值,若有相同的值,则删除第一个)
- 蚁群算法求解旅行商问题 python
- codeforces 405B. Domino Effect
- 马上要考试了,头都大了