美文网首页
Java并发编程实战 - 任务执行与线程池总结

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

作者: 暴躁的伊泽瑞尔 | 来源:发表于2018-11-15 02:19 被阅读0次

第六章 任务执行

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() {/* */}
}

相关文章

  • Java高并发 -- 线程池

    Java高并发 -- 线程池 主要是学习慕课网实战视频《Java并发编程入门与高并发面试》的笔记 在使用线程池后,...

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

    第六章 任务执行 I. 在线程中执行任务 Task执行有两种错误的方式 单线程内串行地执行任务。性能低,若任务有I...

  • 分析jdk-1.8-ForkJoinPool实现原理(上)

    Java并发编程源码分析系列: 分析Java线程池的创建 分析Java线程池执行原理 分析Java线程池Calla...

  • 分析jdk-1.8-ForkJoinPool实现原理(下)

    Java并发编程源码分析系列: 分析Java线程池的创建 分析Java线程池执行原理 分析Java线程池Calla...

  • 分析ReentrantLock的实现原理

    Java并发编程源码分析系列: 分析Java线程池的创建 分析Java线程池执行原理 分析Java线程池Calla...

  • 分析CountDownLatch的实现原理

    Java并发编程源码分析系列: 分析Java线程池的创建 分析Java线程池执行原理 分析Java线程池Calla...

  • 分析Java线程池Callable任务执行原理

    Java并发编程源码分析系列: 分析Java线程池的创建 分析Java线程池执行原理 上一篇分析了线程池的执行原理...

  • 线程

    Java 并发编程:线程池的使用 Java 并发编程:线程池的使用java 多线程核心技术梳理 (附源码) 本文对...

  • 线程池核心知识点

    —读《java并发编程》笔记 为什么需要线程执行框架 不用线程池是否也有异步执行任务的方案? 有两种方案,一是单独...

  • 线程饥饿死锁

    《Java并发编程实践》中对线程饥饿死锁的解释是这样的:在使用线程池执行任务时,如果任务依赖于其他任务,那么就可能...

网友评论

      本文标题:Java并发编程实战 - 任务执行与线程池总结

      本文链接:https://www.haomeiwen.com/subject/vicefqtx.html