线程池的使用

线程池的创建(I)

我们可以通过ThreadPoolExecutor创建一个线程池

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler)

创建线程池时有几个核心参数需要了解下:

  • corePoolSize :线程池的核心数量。当一个任务提交到线程池时,线程池会创建一个线程来执行任务,即使当前有空闲的线程能够执行新任务,线程池还是会创建新的线程,直到需要执行的任务数大于核心数量就不再创建
  • maximumPoolSize:线程池最大数量。线程池所能允许创建的最大线程数,如果队列满了,并且已创建的线程小于最大线程数,线程池还会继续创建线程
  • keepAliveTime:线程活动保持时间。线程池中的线程工作结束后的保持存活的时间,超过这个时间还没有任务执行,线程就会被销毁
  • TimeUnit: 时间单位。线程活动保持时间的时间单位
    在这里插入图片描述
  • workQueue :任务队列,用于保存等待执行的任务的阻塞队列,当任务的数量大于核心数量时,就会将任务暂时保存在任务队列中,如果队列也满了且没超过最大数量,线程池就会创建新的线程执行任务
    有四个阻塞队列可以选择
ArrayBlockingQueue基于数组的有界阻塞队列,按照FIFO的原则
LinkedBlockingDeque基于链表的无界阻塞队列,也可设置界限
SynchronousQueue不存储元素的阻塞队列,每个插入元素必须等到另一个线程调用移除操作,否则会一直阻塞
PriorityBlockingQueue具有优先级的无限阻塞队列
  • handler:拒绝策略。当队列和线程池都满了,说明线程池已经饱和了,当再有新的任务提交时,就要采取一种策略来处理这些新任务,这就是拒绝策略,一共有四种策略
AbortPolicy直接抛出异常
CallerRunsPolicy只用调用者所线程来运行任务
DiscardPolicy不处理,丢弃掉
DiscardOldestPolicy丢弃队列中最近的一个任务,并执行当前的任务

除了上面四个拒绝策略,我们还可以通过实现RejectedExecutionHandler接口来自定义拒绝策略

    public static class MyTask implements Runnable {
        private int i;

        public MyTask(int i) {
            this.i = i;
        }

        @Override
        public void run() {
            System.out.println(System.currentTimeMillis()
                    + ":Thread ID:" + Thread.currentThread().getId()+"  , "+i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String args[]) throws InterruptedException {
        ExecutorService executorService = new ThreadPoolExecutor(2, 2, 0L,
                TimeUnit.SECONDS,
                new PriorityBlockingQueue<>(2),
                (r,executor)-> System.out.println(r.toString() + " 我被抛出5555555")
        );
        for (int i = 0; i < 6; i++) {
            executorService.submit(new MyTask(i));
            Thread.sleep(10);
        }
    }

在这里插入图片描述

线程池的创建(II)

除了通过上面的方式创建线程池,还可以通过Executor框架的工具类Executors创建线程池

通过Executors创建的线程池内部还是使用ThreadPoolExecutor来创建,所以参数与上面的相同就不再赘述

  1. Executors.newFixedThreadPool:可设置线程数的线程池
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

通过参数nThreads设置核心数量和最大数量的大小,阻塞队列为LinkedBlockingQueue没有设置大小,所以是无界的队列,而线程存活时间设置为0L,意味线程一结束任务就被销毁,不会有多余的空闲线程

  1. Executors.newSingleThreadExecutor:使用单个工作线程的线程池
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())

核心数量和最大数量都被设置为1,其余的与FixedThreadPool相同

  1. Executors.newCachedThreadPool:会根据需要创建新线程的线程池
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());

核心数量被设置为0,即为空。最大数量被设置为Integer.MAX_VALUE,即无界的,同时存活时间设置为60秒意味着空闲线程等待新任务的最长时间为60秒,使用没有容量的SynchronousQueue作为线程池的任务队列,意味着如果主线程提交任务速度高于线程处理任务的速度时,线程池就会不断的创建线程,最终可能会导致耗尽资源

为什么建议不用Java提供的线程池创建方法

在《阿里巴巴Java手册》中有一条这样规定。
在这里插入图片描述
通过我们上面所学的可以知道

  • FixedThreadPool和SingleThreadExecutor:这两个线程池的实现方式,我们可以看到它设置的工作队列都是LinkedBlockingQueue,我们知道此队列是一个链表形式的队列,此队列是没有长度限制的,是一个无界队列,那么此时如果有大量请求,就有可能造成OOM。
  • CachedThreadPool:这个线程池的实现方式,我们可以看到它设置的最大线程数都是Integer.MAX_VALUE,那么就相当于允许创建的线程数量为Integer.MAX_VALUE。此时如果有大量请求来的时候也有可能造成OOM。

而使用ThreadPoolExecutor方式去创建线程池,可以让人很好的了解到线程池的使用规则,同时定制程度高

向线程池提交任务和关闭线程池

可以使用exceute()和submint() 方法来提交任务

  • exceute():用于提交不需要返回值的任务
  • submint():用于提交需要返回值的任务

线程池的关闭: 使用 shutdown或shutdownNow 方法来关闭线程池

        ExecutorService executorService = new ThreadPoolExecutor(2, 2, 0L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(2),
                (r,executor)-> System.out.println(r.toString()+"我被抛弃了5555555")
        );
        for (int i = 0; i < 6; i++) {
            executorService.execute(new MyTask(i));//提交任务
            Thread.sleep(10);
        }
        executorService.shutdown();//关闭线程池
    }

学习自Java并发编程的艺术

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lpepsi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值