Java并发编程实战 - 任务执行与线程池总结

第六章 任务执行

I. 在线程中执行任务

  1. Task执行有两种错误的方式
  • 单线程内串行地执行任务。性能低,若任务有IO操作会block住工作线程使用CPU,响应能力差,吞吐量低。
  • 每接收一个任务就为它创建一个线程 ,无限制地创建线程。
  1. 无限制创建线程的缺陷
  • 线程生命周期管理的开销高。相比轻量级任务,线程创建和销毁的操作会显得很浪费。
  • 资源消耗,性能降低。大量的线程占用大量内存并会导致这些线程闲置,对CPU的竞争也会降低性能。
  • 服务稳定性。JVM上有线程总数量限制,超过会抛出OutOfMemoryError。

II. Executor框架

  1. 任务执行不应该和线程的管理耦合在一起。而Executor接口则是对任务提交与任务执行的抽象,它只有一个execute接口。Executor也是基于生产者/消费者模型的。
public interface Executor {
    void execute(Runnable command);
}
  1. Executors中的静态工厂方法可以来创建线程池:
  • newFixedThreadPool。创建固定长度的线程池,线程数量不到要求就会补。
  • newCachedThreadPool。根据需求动态调整线程数量。
  • newSingleThreadExecutor。单线程的Executor。
  • newScheduledThreadPool。创建固定长度的线程池,任务以延迟或定时的方式来执行。
  1. 在一个JVM中,如果Executor不关闭的话,JVM是无法退出的(只要JVM中存在用户工作线程,JVM即无法退出)。为了让Executor自行退出,引入ExecutorService加强生命周期管理
public interface ExecutorService extends Executor {

    /**
    * shutdown()
    * 平缓关闭线程池:不再接收新的任务,等待已经提交的任务跑完。
    */
    void shutdown();

    /**
    * shutdownNow()
    * 粗暴关闭线程池:停止全部任务,然后关闭线程池。
    */
    List<Runnable> shutdownNow();
    boolean isShutdown();
    boolean isTerminated();
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
    // To-be-continued
}
  1. 对于延迟执行和周期执行的任务,Timer类表现不佳。因为Timer是单线程,各个TimerTask启动时间互相干扰,且TimerTask抛出异常会终止线程造成线程泄漏。应该使用DelayQueueScheduledThreadPoolExecutor来构建调度服务。

III. 找出可利用的并行性

  1. Executor框架只用Runnable的问题在于不能返回值或抛出受检异常,所以引入Callable来做到以上两点,以描述一个带返回值的任务。而Future则表示对这个任务生命周期的管理。一般来讲,submit(Callable<T>)会立即返回Future<T>。
public interface Callable<V> {
    V call() throws Exception;
}

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    /**
    * 阻塞直到任务完成。抛出执行Exception的话可以在ExecutionException中拿到。
    */
    V get() throw InterruptedException, ExecutionException, CancellationException;
    V get(long timeout, TimeUnit unit) throw InterruptedException, ExecutionException, CancellationException;
}
  1. 在实现中,提交Callable<T>会被转成FutureTask<T>返回给客户端,也提交给Executor.execute去执行。而FutureTask在客户端作为Future,在线程池端作为Runnable。
  2. 如果需要在运行一组Callable后立即得到结果,可以用一个ExecutorCompletionService来take,阻塞地获得新鲜出炉的结果。其原理是ExecutorCompletionService包装了一个Executor的实例,然后每个执行结束的Task会把结果放进ExecutorCompletionService的BlockingQueue里。类似于装饰器模式。
class ExecutorCompletionService<T> implements CompletionService<T> {
    
    // ...
    private class QueueingFuture extends FutureTask<Void> {
        QueueingFuture(RunnableFuture<V> task) {
            super(task, null);
            this.task = task;
        }
        protected void done() { completionQueue.add(task); }
        private final Future<V> task;
    }

    public ExecutorCompletionService(Executor executor) {
        if (executor == null)
            throw new NullPointerException();
        this.executor = executor;
        this.aes = (executor instanceof AbstractExecutorService) ?
            (AbstractExecutorService) executor : null;
        this.completionQueue = new LinkedBlockingQueue<Future<V>>();
    }
}
  1. 提交的任务可以超时取消,利用Future<T>的get时的timeout参数,会抛出TimeoutException。然后在这里面cancel future。

第七章 取消与关闭

Java没有提供可以安全地终止线程的机制。但是可以通过中断这种写作文机制使一个一个线程终止另外一个线程的工作。

I. 任务取消

  1. 最朴素的使线程退出的方法是set一个boolean,工作的时候定时轮询这个值来判断是否应该中止。但这样的问题是,无法响应阻塞
  2. 应该使用中断的方法取消线程,当中断发生时,会设置中断状态,并在阻塞操作时抛出InterruptedException。
public class Thread {
    /**
    * 中断线程,使阻塞操作抛出Exception
    */
    public void interrupt() {/* */}
    public boolean isInterrupted() {/* */}
    /**
    * 清除当前线程的取消状态
    */
    public static boolean interrupted() {/* */}
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容