Java并发编程---线程池的使用

一般我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?

在Java中可以通过线程池来达到这样的效果。

Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThreads),但是便捷不仅隐藏了复杂性,也为我们埋下了潜在的隐患(OOM,线程耗尽)。

小程序使用这些快捷方法没什么问题,对于服务端需要长期运行的程序,创建线程池应该直接使用ThreadPoolExecutor的构造方法。

Executors中创建线程池的快捷方法,实际上是调用了ThreadPoolExecutor的构造方法(定时任务使用的是ScheduledThreadPoolExecutor),该类构造方法参数列表如下:

// Java线程池的完整构造函数
public ThreadPoolExecutor(
  int corePoolSize, // 核心池的大小
  int maximumPoolSize, // 线程数的上限
  long keepAliveTime, //表示线程没有任务执行时最多保持多久时间会终止
  TimeUnit unit, // 参数keepAliveTime的时间单位,有7种取值:
  BlockingQueue<Runnable> workQueue, // 一个阻塞队列,用来存储等待执行的任务
  ThreadFactory threadFactory, // 线程工厂,主要用来创建线程
  RejectedExecutionHandler handler) // 拒绝策略

这些参数中,比较容易引起问题的有corePoolSize, maximumPoolSize, workQueue以及handler:

  • corePoolSize和maximumPoolSize设置不当会影响效率,甚至耗尽线程;
  • workQueue设置不当容易导致OOM;
  • handler设置不当会导致提交任务时抛出异常。

线程池的工作顺序:corePoolSize -> 任务队列 -> maximumPoolSize -> 拒绝策略

Runnable和Callable

可以向线程池提交的任务有两种:Runnable和Callable,二者的区别如下:

  • 方法签名不同,void Runnable.run(), V Callable.call() throws Exception
  • 是否允许有返回值,Callable允许有返回值
  • 是否允许抛出异常,Callable允许抛出异常。

Callable是JDK1.5时加入的接口,作为Runnable的一种补充,允许有返回值,允许抛出异常。

线程池流程

Java并发编程---线程池的使用_第1张图片

execute()和submit()方法
  1. execute(),执行一个任务,没有返回值。
  2. submit(),提交一个线程任务,有返回值。
  3. submit(Callable task)能获取到它的返回值,通过future.get()获取(阻塞直到任务执行完)。一般使用FutureTask+Callable配合使(IntentService中有体现)。
  4. submit(Runnable task, T result)能通过传入的载体result间接获得线程的返回值。
  5. submit(Runnable task)则是没有返回值的,就算获取它的返回值也是null。
  6. Future.get方法会使取结果的线程进入阻塞状态,知道线程执行完成之后,唤醒取结果的线程,然后返回结果。
附注:

Java通过Executors默认提供四种线程池,分别为:

  1. newSingleThreadExecutor
    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

  2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  3. newScheduledThreadPool 创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。

  4. newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

  5. newWorkStealingPool,这个是 JDK1.8版本加入的一种线程池,适合使用在很耗时的操作,但是newWorkStealingPool不是ThreadPoolExecutor的扩展,它是新的线程池类ForkJoinPool的扩展,但是都是在统一的一个Executors类中实现,由于能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中。

你可能感兴趣的:(Java,进阶,java,并发编程,线程池,多线程)