Java线程池的相关使用

1.Executor的UML图

ScheduledThreadPoolExecutor类继承ThreadPoolExecutor,实现ScheduledExecutorService接口,ThreadPoolExecutor类继承AbstractExecutorService类,AbstractExecutorService类实现ExecutorService接口,ScheduledExecutorService接口实现ExecutorService接口,ExecutorService接口实现Executor接口。

2.创建线程池的方式

   1.预定义线程池:使用Executors提供的方法创建线程池

  • Executors.newFixedThreadPool(int nThreads):创建固定大小的线程池

       提交一个任务创建一个线程,直至到达固定大小后不变,此时新提交的任务需等待至有空闲线程才会执行,空闲线程不会回         收。

  • Executors.newSingleThreadExecutor():创建单个线程的线程池

       线程池内只有一个线程,一个时间段只能执行一个任务。提交多个任务时按提交顺序顺序执行。

  • Executors.newCachedThreadPool():创建可缓存的线程池

        线程池上限无限大(实际最大为Integer.MAX_VALUE),提交一个任务时如果线程池内有空闲线程则复用该线程,没用则创建          新线程;但空闲线程超过最大等待时间(默认60s)还没有新任务提交时,则会回收该线程。

  • Executors.newScheduledThreadPool(int corePoolSize):创建可调度(任务延时执行与任务周期性执行)的线程池

       任务延时执行:

       ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);

       scheduledExecutorService.schedule(task1,2, TimeUnit.SECONDS);

       scheduledExecutorService.schedule(task2,3, TimeUnit.SECONDS);

       上述创建了2个基本线程的可调度的线程池大小,task1任务会在2s后执行,task2任务会在3s后执行。

       任务周期性执行:

       scheduledExecutorService.scheduleAtFixedRate(task1,1,2,TimeUnit.SECONDS.SECONDS);//1s后以2s为周期执行task1;

       scheduledExecutorService.scheduleWithFixedDelay(task2,1,2,TimeUnit.SECONDS.SECONDS);//1s后以2s为周期延时1秒         执行task2。

   2.自定义线程池:使用ThreadPoolExecutor创建线程池

  ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
参数名作用
corePoolSize核心线程池大小
maximumPoolSize最大线程池大小
keepAliveTime线程池中超过corePoolSize数目的空闲线程最大存活时间;可以allowCoreThreadTimeOut(true)使得核心线程有效时间
unit时间单位
workQueue阻塞任务队列
threadFactory新建线程工厂

handler

拒绝策略:当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理

阻塞任务队列workQueue种类:

  • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
  • LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
  • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
  • PriorityBlockingQueue:一个具有优先级的无限阻塞队列

拒绝策略RejectedExecutionHandler种类:

  • AbortPolicy:该策略是线程池默认策略;如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
  • DiscardPolicy:如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常。
  • DiscarOldestPolicy:如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列。
  • CallRunsPolicy:如果添加到线程池失败,那么主线程会自己取执行该任务,不会等待线程池的线程取执行。
  • 自定义策略
import java.util.Date;
import java.util.concurrent.*;

/**
 * ClassName:ThreadPool
 * Description:线程池的相关使用
 * Author:YMJ
 * Date:2020-07-14 19:57
 * Version:V1.0
 * 《阿里Java开发手册》
 * 【强制】新建线程时,不允许在应用中自行显式创建线程,必须通过线程池提供(AsyncTask 或者ThreadPoolExecutor或者其他形式自定义的线程池)
 * 【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险
 */
public class ThreadPoolTest {
    //线程工厂所要创建的线程类
    public static class MyThread extends Thread {
        public MyThread(Runnable r){
            super(r);
        }

        public MyThread(Runnable r,String name) {
            super(r, name);
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            Date startDate = new Date();
            super.run();
            Date finishDate = new Date();
            System.out.println(threadName+"运行耗时:"+(finishDate.getTime()-startDate.getTime()));
        }
    }
    //线程所要执行的任务类
    public static class MyTask implements Callable<Object>{

        @Override
        public Object call() throws Exception {
            System.out.println(Thread.currentThread().getName()+"任务进行中");
            Thread.sleep(200);
            return null;
        }
    }

    public static class MyThreadFactory implements ThreadFactory{
        private static int threadCount = 0;
        @Override
        public Thread newThread(Runnable r) {
            threadCount++;
            MyThread th = new MyThread(r,"线程"+ Integer.valueOf(threadCount).toString());
            return th;
        }
    }

    public static void main(String[] args) {
        LinkedBlockingQueue<Runnable> linkedBlockingQueue = new LinkedBlockingQueue<Runnable>(4);//没有指定大小时默认为Integer.MAX_VALUE
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,
                6,
                5,
                TimeUnit.SECONDS,
                linkedBlockingQueue,
                new MyThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        //创建了一个核心线程数为2,最大线程数为6,空闲等待时间为5s,阻塞队列大小为4的链式阻塞队列,使用自定义线程工厂,采取默认拒绝策略的线程池
        MyTask mytask = new MyTask();
        for (int i = 1; i <= 15; i++) {
            threadPool.submit(mytask);
            System.out.println("阻塞队列大小:"+linkedBlockingQueue.size());
        }
        threadPool.shutdown();
    }
}

 执行流程:

1.创建了一个核心线程数为2,最大线程数为6,空闲等待时间为5s,阻塞队列大小为4的链式阻塞队列,使用自定义线程工厂,采取默认拒绝策略的线程池

2.当一次性提交15个任务时,首先执行任务1和2,当超过了corepoolsize大小时会将任务放入阻塞队列中,由于阻塞队列大小为4,于是任务3,4,5,6被放入阻塞队列

3.然后阻塞队列满了后,当前线程池内线程数如果还未超过最大线程数maximumpoosize时,线程池继续创建线程,直至线程池内线程数达到最大线程数。因此线程池还能继续创建4个线程去执行任务7,8,9,10

4.当阻塞队列已满并且线程池内线程数达到最大线程数时,则会触发拒绝策略;本例采用默认拒绝策略AbortPolicy,因此程序将抛出RejectedExecutionException异常。因此任务10,11,12,13,14,15将被丢弃并且抛出异常

线程池总容量=最大线程数+阻塞队列大小,超过后执行拒绝策略

 

线程池提交任务·execute()与submit()的区别:

1.execute只能添加Runnable任务,submit可以提交Runnable任务与Callable任务

2.execute无返回结果,submit有返回结果(即使添加的任务是Runnable任务也会有返回结果,虽然无意义)

3.发生异常execute会终止进程,而submit可以捕获异常

 

在使用线程池的时候推荐使用ThreadPoolExecutor来创建,这样一是可以更加明确线程池的运行规则,二是由于executors提供的创建线程池的方法中默认的maximumPoolSize都是Integer.MAX_VALUE,这样会导致堆积大量的请求或者创建大量的线程,造成资源耗尽OOM(out of memory)。

因此《阿里java开发手册》才有此规定:

* 【强制】新建线程时,不允许在应用中自行显式创建线程,必须通过线程池提供(AsyncTask 或者ThreadPoolExecutor或者其他形式自定义的线程池)
* 【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值