Java 线程池使用总结

基本知识

Runnable接口

Runnable接口只有一个run()方法 ,并且该方法无返回值

public interface Runnable {  
    
    public abstract void run();
}
Callable接口

Callable接口也只有一个call()方法,但是该方法有具体的返回值

public interface Callable<V> {
    
    V call() throws Exception;
}

Callable与Runnable的区别:

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

  2. Callable的任务执行后可返回值,而Runnable的任务是不能返回值的

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

  4. 运行Callable任务可拿到一个Future对象

Future接口

Future接口一共有5个方法


public interface Future<V> {
	// 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
    boolean cancel(boolean mayInterruptIfRunning); 
    
	// 任务是否已经取消,任务正常完成前将其取消,则返回 true
    boolean isCancelled();
    
    // 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
    boolean isDone();
    
 	// 等待任务执行结束,然后获得V类型的结果。
    // InterruptedException 线程被中断异常, ExecutionException任务执行异常,
    // 如果任务被取消,还会抛出CancellationException
    V get() throws InterruptedException, ExecutionException;
    
    // 同上面的get功能一样,多了设置超时时间。
    // 参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。
    // 如果计算超时,将抛出TimeoutException
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Future的使用场景:

Executor是Runnable和Callable的调度容器,Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作,当get()方法被调用时,线程会阻塞,直到当前线程执行完成,释放锁。


ThreadPoolExecutor构造

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,  TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
  • corePoolSize: 线程池核心线程数

  • maximumPoolSize:线程池最大数

  • keepAliveTime: 空闲线程存活时间

  • unit: 时间单位

  • workQueue: 线程池所使用的缓冲队列

  • threadFactory:线程池创建线程使用的工厂

  • handler: 线程池对拒绝任务的处理策略RejectedExecutionHandler来处理;ThreadPoolExecutor内部有实现4个拒绝策略,默认为AbortPolicy策略:

    • CallerRunsPolicy:由调用execute方法提交任务的线程来执行这个任务
    • AbortPolicy:抛出异常RejectedExecutionException拒绝提交任务
    • DiscardPolicy:直接抛弃任务,不做任何处理
    • DiscardOldestPolicy:去除任务队列中的第一个任务,重新提交
线程池规则:
  • 假设任务队列没有大小限制
    • 如果线程数量<=核心线程数量,那么直接启动一个核心线程来执行任务,不会放入队列中
    • 如果线程数量>核心线程数,但<=最大线程数,并且任务队列是LinkedBlockingDeque的时候,超过核心线程数量的任务会放在任务队列中排队
    • 如果线程数量>核心线程数,但<=最大线程数,并且任务队列是SynchronousQueue的时候,线程池会创建新线程执行任务,这些任务也不会被放在任务队列中。这些线程属于非核心线程,在任务完成后,闲置时间达到了超时时间就会被清除
    • 如果线程数量>核心线程数,并且>最大线程数,当任务队列是LinkedBlockingDeque,会将超过核心线程的任务放在任务队列中排队。也就是当任务队列是LinkedBlockingDeque并且没有大小限制时,线程池的最大线程数设置是无效的,他的线程数最多不会超过核心线程数
    • 如果线程数量>核心线程数,并且>最大线程数,当任务队列是SynchronousQueue的时候,会因为线程池拒绝添加任务而抛出异常
  • 任务队列大小有限时
    • 当LinkedBlockingDeque塞满时,新增的任务会直接创建新线程来执行,当创建的线程数量超过最大线程数量时会抛异常。
    • SynchronousQueue没有数量限制。因为他根本不保持这些任务,而是直接交给线程池去执行。当任务数量超过最大线程数时会直接抛异常。

ExecutorService使用线程池

ExecutorService的构造

ExecutorService是Java中对线程池定义的一个接口,Java API对ExecutorService接口的实现有两个,所以这两个即是Java线程池具体实现类:

ThreadPoolExecutor
ScheduledThreadPoolExecutor

创建一个什么样的ExecutorService的实例(即线程池)需要根据具体应用场景而定,不过Java给我们提供了一个Executors工厂类,它可以帮助我们很方便的创建各种类型ExecutorService线程池,Executors一共可以创建下面这四类线程池:

// 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
newCachedThreadPool
    
// 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
newFixedThreadPool 
    
// 创建一个定长线程池,支持定时及周期性任务执行
newScheduledThreadPool 
    
// 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
newSingleThreadExecutor 

也可以根据ThreadPoolExecutor的构造方法自定义合适的ExecutorService

ExecutorService的执行:
  • execute(Runnable): 接收一个Runnable实例,并且异步的执行

  • submit(Runnable): 返回一个Future对象,通过返回的Future对象,我们可以检查提交的任务是否执行完毕

  • submit(Callable): 返回一个Future对象

  • invokeAny(…): 执行这个方法不会返回Future,但是会返回所有Callable任务中其中一个任务的执行结果

  • invokeAll(…): 返回一个Future的List,其中对应着每个Callable任务执行后的Future对象

ExecutorService的执行关闭:
  • shutdown(): ExecutorService不会立即关闭,但是它不再接收新的任务,直到当前所有线程执行完成才会关闭,所有在shutdown()执行之前提交的任务都会被执行
  • shutdownNow(): 跳过所有正在执行的任务和被提交还没有执行的任务, 会返回从未被执行的线程队列。但是它并不对正在执行的任务做任何保证,有可能它们都会停止,也有可能执行完成

阻塞等待线程池执行完毕

如果线程池嵌入在业务代码中,需要等待线程池执行完,再执行后续操作,需要阻塞等待

  • 使用CountDownLatch

    这是一个计数器操作,在线程池执行之前,给计数器指定数值(与要执行代码的次数一致)也就是threads.size(),在线程池执行代码体里面要加上countDownLatch.countDown();代表每执行一次数值减少一,最后在循环体外边写上countDownLatch.await();代表等待计数器归零。

  • 使用Future.get()

    Future.get()可以同步等待线程执行完成,并且可以监听执行结果

  • 使用shutdown()

    如果线程池是方法内部创建的,可以直接使用shutdown()也会等待线程池的执行结果。同时会关闭线程池资源:

    executor.shutdown();
    try {
        executor.awaitTermination(300,TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值