并发编程笔记四:java线程池初探

内容多有疏漏,有问题欢迎提出

目录

  1. 什么是线程池
  2. JDK对线程池的支持
  3. 线程池的实现
  4. 拒绝策略
  5. 总结

什么是线程池

上一节我们讲到java的线程,在实际的项目应用中,如果只是对线程创建并使用,有可能会导致以下三个问题:

  1. 占用大量CPU资源
  2. 创建和销毁线程的时间过长
  3. 占用大量内存

所以我们需要对线程们加以管控,而管控线程的工具就叫做线程池。简单来讲,线程池的作用就是把创建线程变成了从线程池中获得空闲线程,关闭线程变成了向线程池归还线程。

JDK对线程池的支持

在JDK中,提供了一套Executor框架,在java.util.concurrent包中,其中ThreadPoolExecutor表示一个线程池。Executors类则扮演着线程池工厂的角色,通过Executors可以取得一个拥有特定功能的线程池,ThreadPoolExecutor类实现了Executor接口,因此通过这个接口,任何Runnable对象都可以被ThreadPoolExecurot线程池调度。

其中,工厂方法主要有以下几种:

  • newFixedThreadPool(),该方法返回一个固定数量的线程池。该线程池中线程的数量始终不变,当有新任务提交时,线程池中若有空闲线程,则立即执行,如果没有,则新的任务会被暂存到一个任务队列中,带有空闲线程是,便处理任务队列中的任务。
  • newFixedThreadPool(),该方法返回一个单例的线程池。
  • newCachedThreadPool(),该方法会返回一个可以根据实际情况调整线程数量的线程池。由于线程数量不确定,当有空闲线程时,线程可以复用,若所有的线程均在工作,又有新的任务提交,则会创建新的线程处理任务。

下面以newFixedThreadPool()为例,简单的展示线程池的使用方法:

public class ThreadPoolTest {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World");
            }
        });
        //创建线程池
        ExecutorService es  = Executors.newFixedThreadPool(5);
        for(int i=0;i<10;i++){
            es.submit(t);
        }
        //关闭线程池
        es.shutdown();
    }
}

线程池的实现

线程池无论是newFixedThreadPool()、newFixedThreadPool()还是newCachedThreadPool(),其内部均是使用了ThreadPooleExecuor类同样以newFixedThreadPool()为例:

 

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue(),
                                  threadFactory);
}

ThreadPooleExecuor类的构造函数为:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

其中构造函数的参数定义如下:

  • corePoolSize:制定了线程池中的线程数量;
  • maximumPoolSize:制定了线程池中的最大线程数量;
  • keepAliveTime:当线程池线程数量超过corePoolSize时,多余的空闲线程的存活时间,即超过corePoolSize的空闲线程,在多长时间内会被销毁;
  • unit:keepAliveTime的单位;
  • workQueue:任务队列,被提交但尚未被执行的任务;
  • threadFactory:线程工厂,用于创建线程,一般用默认的即可;
  • handler:拒绝策略,当任务太多来不及处理时,如何拒绝任务;

拒绝策略

  • AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作
  • CallerRunsPolicy策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务,显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降
  • DiscardOldestPolicy策略:该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务
  • DiscardPolicy策略:该策略默默地丢弃无法处理的任务,不予任何处理

总结

以上内容讲解了线程池的作用,线程池的实现案例以及线程池的简单实现逻辑。下一章,我们会讲jdk的并发容器,敬请期待~

你可能感兴趣的:(原创,java,并发,java并发编程)