Java 线程池 个人总结的一点笔记

转载请标明出处: 

http://blog.csdn.net/mollyxiong/article/details/54943942;

本文出自:【MollyShong博客】


一、为什么要用线程池?

    应用程序中多个地方需要开启线程执行任务,每次开启和销毁线程需要大量的时间和系统资源,使用线程池的好处就是让我们能重用这些线程,节省时间以及系统资源,提高程序运行效率。

    除了开启和销毁线程外,活动的线程也需要消耗资源,如果JVM虚拟机里创建过多线程可能会导致系统过度消耗内存导致系统资源不足,可以用检测cpu个数来限定创建活动线程个数:

private static final int count = Runtime.getRuntime().availableProcessors() * 3 + 2;

获取JVM可用的处理器个数API:Runtime.getRuntime().availableProcessors()

或者使用cpu个数+1 可以达到CPU最大使用率


二、线程池的组成

线程池的组成部分 线程池管理器,工作线程,任务队列,任务接口

    线程池管理器:作用是创建,销毁并管理线程

    工作线程:是用来接受处理来自任务队列中的任务,无任务执行时处于等待状态

    任务队列:把工作线程来不及处理的任务缓冲到队列中,等待线程处理

    任务接口:所有的任务必须实现的接口,也就是任务要做的事情、入口、执行状态等等


三、什么时候用线程池

线程池的使用场景,当应用程序需要执行大量短而小的任务时,可以使用线程池技术降低系统创建销毁线程的时间和资源,循环重用线程去执行这些短而小的任务,不必每次都创建新的线程去执行任务。

但是,当任务执行的时间比较长时使用线程池来提升运行效率的效果也是不大的。


四、规避线程池使用风险的建议

    1.避免对需要使用其他任务执行结果的任务进行排队,如果层层依赖上一个任务,会导致所有的线程都很忙,执行效率下降

    2.为相同任务类型的任务分配相同的工作队列

    3.如果任务执行需要较长时间,请为其设定最长等待时间,防止线程泄漏(线程从线程池出去了,就回不来了)

    4.根据CPU个数+1来指定线程池的大小,是CPU达到最大利用率

    

线程池使用的介绍

    1.ThreadPoolExecutor构造方法


/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters and default thread factory.
 *
 * @param corePoolSize the number of threads to keep in the pool, even
 *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
 * @param maximumPoolSize the maximum number of threads to allow in the
 *        pool
 * @param keepAliveTime when the number of threads is greater than
 *        the core, this is the maximum time that excess idle threads
 *        will wait for new tasks before terminating.
 * @param unit the time unit for the {@code keepAliveTime} argument
 * @param workQueue the queue to use for holding tasks before they are
 *        executed.  This queue will hold only the {@code Runnable}
 *        tasks submitted by the {@code execute} method.
 * @param handler the handler to use when execution is blocked
 *        because the thread bounds and queue capacities are reached
 * @throws IllegalArgumentException if one of the following holds:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue}
 *         or {@code handler} is null
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}

- corePoolSize:线程池维护线程的最少数量

- maximumPoolSize:线程池维护线程的最大数量

- keepAliveTime: 线程池维护线程所允许的空闲时间,这是多余(超出corePoolSize大小是多余的)空闲线程在终止之前等待新任务的最大时间。

- unit: 线程池维护线程所允许的空闲时间的单位

- workQueue: 线程池所使用的缓冲队列,存放由execute方法提交的任务

- handler: 线程池对拒绝任务的处理策略(下面四种策略)

    1.ThreadPoolExecutor.AbortPolicy(抛出java.util.concurrent.RejectedExecutionException异常)

    2.重新尝试添加此任务,自动执行execute方法(ThreadPoolExecutor.CallerRunsPolicy)

    3.抛弃旧的任务(ThreadPoolExecutor.DiscardOlddestPolicy)

    4.抛弃当前的任务(ThreadPoolExecutor.DiscardPolicy)

------------------------------------------------------------------------------------

Execute() 和 submit() 都是提交任务的方法, 区别是什么?

Execute()  

    1.只能接受实现了Runnable类型的参数

    2.没有返回值

submit()    

    1.可以接受Runnable 和Callable<String> 类型的参数,但是Runnable返回的Future.get()方法返回的会是null

    2.有Future<String> 返回的参数 .get()方法可以获取 任务执行的返回值 通常是Callable类型的任务,因为这个类型中call方法是有返回值的

    3.可以捕获任务执行中的异常,在Future.get()方法来捕获

    意思就是如果你在你的task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,

通过捕获Future.get抛出的异常。 

比如说,我有很多更新各种数据的task,我希望如果其中一个task失败,其它的task就不需要执行了。那我就需要catch Future.get抛出的异常,然后终止其它task的执行

------------------------------------------------------------------------------------

线程池执行任务的方法大致有这几种

  • execute(Runnable)  

  • submit(Runnable)  

  • submit(Callable)  

  • invokeAny(...)  

  • invokeAll(...) 

execute(Runnable)  接收一个 java.lang.Runnable 对象作为参数,在线程池中某个线程中异步执行这个任务,方法没有返回值,无法观察任务执行的返回结果

submit(Runnable)    也是接收一个  java.lang.Runnable 对象作为参数,会返回一个Future对象的执行结果,用get方法获取,执行完成get会返回一个null

submit(Callable)      方法 submit(Callable) 和方法 submit(Runnable) 比较类似,但是区别则在于它们接收不同的参数类型。Callable 的实例与 Runnable 的实例很类似,

但是 Callable 的 call() 方法可以返回一个结果。方法 Runnable.run() 则不能返回结果。执行结果从返回的future.get方法获取,结果是用call()方法中返回的

invokeAny(...)          方法接受一个Callable类型的集合作为参数,不会返回future,但是返回的是集合中任意一个callable的执行结果,如果某一个callable抛出异常会取消其

他Callable的执行

使用方法:

Set<Callable<String>> callables = new HashSet<Callable<String>>();  

callables.add(new Callable<String>() {  

    public String call() throws Exception {  

        return "Task 1";  

    }  

});  

String result = executorService.invokeAny(callables);  

invokeAll(...)  方法同样接受一个Callable类型的集合作为参数,返回的是Future的集合,可能会因为异常终止所有的运行

------------------------------------------------------------------------------------

线程池的关闭

shutdown,执行后不再接收新任务,否则继续添加任务会抛出RejectedExecutionException异常,如果里面有任务,就执行完。

shutdownNow,执行后不再接受新任务,如果有等待任务,移出队列;有正在执行的,尝试停止之,试图终止的方法是调用Thread.interrupt(),所以如果线程中没有sleep 、

wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。执行shutdownNow方法可能不会立即终止。

executorService 提供了上面这两种方法来关闭池中的所有线程以及线程池自身。

在程序结束的时候一定要调用关闭的方法,否则如果还有活动的线程会阻止java虚拟机关闭

boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

阻止所有任务在关机请求后完成执行 ,或超时发生,或当前线程被中断,以最先发生为准

boolean isTerminated();

如果关闭后所有任务都已经完成,返回ture。除非先调用shutdown或shutdownNow,否则永远不返回true

------------------------------------------------------------------------------------

 Callable 和 Future接口

  Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。   Callable和Runnable有几点不同:      

(1)Callable规定的方法是call(),而Runnable规定的方法是run().      

(2)Callable的任务执行后可返回值(任意类型),而Runnable的任务是不能返回值的。     

(3)call()方法可抛出异常,而run()方法是不能抛出异常的。      

(4)运行Callable任务可拿到一个Future对象,   Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。   通过Future对象可了解

任务执行情况,可取消任务的执行,还可获取任务执行的结果。   

// 提交并执行任务,任务启动时返回了一个 Future对象, 

// 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作 

Future future1 = es.submit(task1); 

// 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行 

System.out.println("task1: " + future1.get());

Future future2 = es.submit(task2);

Future常用方法解析

    boolean cancel(Boolean mayInterruptlfRunning): 传入true尝试取消正在执行的任务,false允许该任务完成;如果取消成功返回ture,否则返回false。其实是调用

interrupt()方法中断线程。

    boolean isCancelled():如果在Callable任务正常完成前被取消,则返回true。

    boolean isDone();        如果任务执行完毕 返回ture 不论是正常执行完毕、异常、还是手动取消、只要完成了都返回ture。

    <?> get()  throws InterruptedException, ExecutionException :返回Callable任务里的call方法的返回值,调用该方法将导致线程阻塞,必须等到子线程结束才得到返回值,但是并不会妨碍其他任务的执行。线程被中断会抛出InterruptedException异常,任务执行中出现错误会抛出ExecutionException异常,如果任务被取消,还会抛出CancellationException 异常。

    <?> get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException :返回Callable任务里的call方法的返回值,该方法让程序最多阻塞timeout和unit指定的时间,如果经过指定时间后Callable任务依然没有返回值,将会抛出TimeoutException,其他与上面的get()方法用法一样。

Executors 工厂方法创建线程池的5种方式

1.newFixedThreadPool() : 返回一个固定线程数的线程池,自始至终线程池大小不变,添加任务时当没有空闲线程时任务会被存放在任务队列workQueue中,采用先进先出的执行顺序。

2.newCachedThreadPool():返回的线程池可以动态的调整大小,如果所有线程都在工作,当有新的任务添加进来时则需要重新创建线程来执行任务。当线程执行完任务会有默认60s的活动等待期(等待期可以配置,超过设定时间会停止该线程),有新的任务添加时,则复用空闲的线程去执行。

3.newSingleThreadExecutor():返回的线程池是只有一个线程,每次只能执行一个任务,添加的多余的任务会放在任务队列workQueue中,任务执行同样也是使用先进先出的顺序。

4.newScheduledThreadPool() :返回的线程池可以控制线程的定时执行或周期性执行某任务,可以任意指定池的大小

5.newSingleThreadScheduledExecutor() :  返回的线程池可以控制线程的定时执行或周期性执行某任务,和上面的区别是这个大小只能是1

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值