天天看点

Java并发编程艺术 9 Java中的线程池

第9章  Java中的线程池

线程池的优点 【1】降低资源消耗。通过重复利用已创建的线程,可以减少创建、销毁线程的消耗 【2】提高响应速度。可以直接使用已创建线程。 【3】提高线程的管理性。通过线程池统一的分配、调优和监控。

线程池创建 new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue) 最少参数      {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,              Executors.defaultThreadFactory(), defaultHandler);      }

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)最多参数

int corePoolSize  线程池的基本的大小,当提交一个任务到线程池,线程池会创建一个线程来执行任务。即使有空闲线程也会创建新的线程。线程数到达corePoolSize大小时,就不在创建新的。如果使用线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。 int maximumPoolSize 线程池最大数量,线程池允许创建的最大数量。如果任务队列已经满了,并且已创建的线程数小于maximumPoolSize,线程池会再创建新的线程。如果阻塞队列是无界队列,这个参数就没软用了。 long keepAliveTime 线程保持活动时间,工作线程空闲后保持的存活时间。 TimeUnit unit       线程保持活动时间的单位。可选DAYS天、HOURS小时、MINUTES分钟、MILLISECOUNDS毫秒 BlockingQueue<Runnable> workQueue   用于保存等待执行的任务的阻塞队列。可以选择ArrayBlockingQueue、LinkedBlockingQueue等 ThreadFactory threadFactory   用于设置创建线程的工厂,可以通过线程工厂为每个线程设置有意义的名字。使用了开源框架的guava提供的ThreadFactoryBuilder。 RejectedExecutionHandler handler   饱和策略。当队列和线程池饱和的情况下,采用的提交策略。默认是AbortPolicy

Java并发编程艺术 9 Java中的线程池

==========================================================================================================================

Java并发编程艺术 9 Java中的线程池

 ---------- 

提交任务:submit和execute

可以通过submit()和execute()方法提交任务。 区别: 【1】execute()是在Executor接口中定义,也是Executor中唯一的抽象方法。submit()是在ExecutorService中定义的(继承了Executor接口) 【2】execute()方法没有返回值,submit()返回一个Future对象。通过Future.get可以判断任务是否执行成功、或者执行完成。(主要区别) 【3】submit()方便主线程对Exception做处理。通过Future.get捕获抛出异常。 注意:Future.get方法是阻塞的。

void execute(Runnable command)  : 在将来某个时间执行给定任务。可以在新线程中或者在现有池线程中执行该任务。如果无法将任务提交执行,或者因为此执行程序已关闭,或者因为已达到其容量,则该任务由当前 RejectedExecutionHandler处理。 submit被重载三次 Future<?> submit(Runnable task)  提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功完成时将会返回null。如果抛出异常,get方法将捕获异常。 Future submit(Runnable task,T result)  Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功完成时将会返回给定的结果(也就是预先传递的result参数)。如果抛出异常,get方法将捕获异常。 submit(task,null)和 submit(task)是一样的。 Future submit(Callable task)  提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。该 Future 的 get 方法在成功完成时将会返回该任务的结果。 如果想立即阻塞任务的等待,则可以使用 result = exec.submit(aCallable).get(); 

public static void main(String[] args) throws InterruptedException, ExecutionException {      BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();

     ThreadPoolExecutor e= new ThreadPoolExecutor(20, 50, 1L, TimeUnit.MINUTES, workQueue); //   Future<?> f = e.submit(new MyRunnable(),new Re());      Future<String> f = e.submit(new MyCallable());      System.out.println("future get : "+ f.get()); }

static class MyRunnable implements Runnable{      public void run() {            String name = "mm";            System.out.println(name);            throw new RuntimeException("haha");

     }

}

static class MyCallable implements Callable<String>{      public String call() throws Exception {            String name = "dd";            System.out.println(name);

           Thread.sleep(5000);

           return name;      }

}

static class Re{      String re = "ok"; } 【1】execute方法性能较高,所以不需要返回值的时候尽量可以选用。 【2】如果需要返回值,可以实现Callable接口。如果只是需要得到空的结果使用submit就好。 【3】Callable和Future使用可以实现异步执行方法,异步获取执行结果。

关闭线程池 通过调用shutdown或者shutdownNow方法来关闭线程池。原理是遍历线程池中的工作线程,然后逐个调用interrupt方法来中断线程,所以无法响应中断的线程可能永远不能停止。 shutdownNow:将线程池状态设置为STOP,停止所有正在执行或空闲的线程,并返回等待执行任务的列表。 shutdown:只是将线程状态设置为SHUTDOWN,然后终端空闲的线程。

调用了以上的任意一个,isShutdown都会返回true。当所有任务都关闭以后才算线程池关闭成功,isTerminated方法才返回true。通常调用shutdown方法,如果任务不执行完也行,也可以使用shutdownNow。

合理配置线程池 1.判断任务性质:CPU密集型、IO密集型、混合型 2.可以选择使用优先级队列处理优先级不同的任务。(注意:可以导致低优先级的任务永远不执行) 3.建议使用有界队列。可以增加系统的稳定性和预警能力,维持在一定的任务数量,可以设置为几千。

线程池的监控 在系统中使用了大量线程池,有必要对线程池进行监控,可以根据线程池使用情况快速定位问题。 taskCount:线程需要执行的任务数量 completedTaskCount:线程池已完成的任务书,小于等于taskCount largestPoolSize:线程池曾经创建的最大线程数。可以用来判断线程池是否满过。 getPoolSize:线程池的线程数量。线程池不销毁的话,线程池中的线程不会自动销毁,所以这个数值只增不减。

还可以对线程池进行扩展。通过集成线程池,重写线程池的beforeExecute、afterExecute和terminated方法。执行一些自定义的代码来监控。 例如监控任务的平均时间,最大(最小)执行时间等。在线程池中默认是空方法。

继续阅读