java并发相关的线程池

一、ThreadPoolExecutor类

ThreadPoolExecutor是一个线程池的执行类

其相关的参数为:

//第一个参数:核心线程的个数

int corePoolSize = 3;

//第二个参数 最大线程个数

int maximumPoolSize = 10;

//第三个参数 线程的存活时间(一般是非核心线程的可存活时间, 但是如果设置了allowCoreThreadTimeOut为true 也会影响核心线程)

long keepAliveTime = 10L; //非核心线程的存活时间

//第四个参数 指定第三个时间参数的单位

TimeUnit unit = TimeUnit.SECONDS;

//第五个参数 线程池中的任务队列(用于保存未消耗的线程对象)

 

ThreadPoolExecutor常见的操作主要有以下几个方法:

  1. getPoolSize():返回线程池实际的线程数。
  2. getActiveCount():返回在执行者中正在执行任务的线程数。
  3. getCompletedTaskCount():返回执行者完成的任务数。
  4. submit(): 提交一个线程给线程执行者,如果执行者有空余线程,则直接执行;否则等待直到有空闲线程。这里调用sumbit后,并不会阻塞调用线程。调用者所在的线程和执行的线程并发运行。
  5. execute(runbale)加入并执行相关的线程
  6. shutdown(): 调用这个方法后,线程执行者在完成当前已经提交的所有任务后,结束运行。 a. 在主线程中如果调用线程执行者的这个方法,并不会使线程执行者中已经submit的任务中断(无论是待执行、执行中)  b. 调用shutdown会通知执行者,后面提交的任务“不允许接受”,在shutdown后提交任务会抛RejectedExecutionException  的异常信息。
  7.  shutdownNow(): 调用这个方法后,立即停止执行者的运行。返回待执行的Task。 a. 中断所有正在执行的线程: 正在执行的线程会收到中断信息。抛出InterruptedException b. 后面提交的任务“不允许接受”,在shutdownNow后再调用submit提交任务,会抛出RejectedExecutionException的异常息。

二、ThreadPoolExecutor的使用

1、当任务线程数量<=核心线程数时 则每一个任务线程都会被核心线程执行,不会新建线程执行,队列中没有任务 无论任务队列采用哪种方式实现

2、当任务线程数>核心线程,且<=最大线程数 分三种情况

2.1 使用LinkedBlockingQueue队列时(自动扩容,没有大小区分) 则会将超过核心线程的线程任务先放置到队列中,等到核心线程有空闲的时候会从队列中获取任务。

2.2 使用SynchronousQueue时队列是,则会创建相同数量的线程和核心线程(新创建的线程+核心线程=任务线程 任务线程不大于线程池的最大线程数)一起去执行线程任务 SynchronousQueue内部并没有数据缓存空间

详细解释请参考:http://ifeve.com/java-synchronousqueue/

2.3 使用ArrayBlockingQueue队列(可以显示的设置队列长度)

长度不限 通过核心线程去消费线程任务,剩余的线程任务放置到队列中且最终由核心线程消费。

//运行的线程数大于核心线程 但是小于最大线程数的三种队列的不同处理形式

@Test

public void testOne(){

//第一个参数:核心线程的个数

int corePoolSize = 3;

//第二个参数 最大线程个数

int maximumPoolSize = 10;

//第三个参数 线程的存活时间(一般是非核心线程的可存活时间, 但是如果设置了allowCoreThreadTimeOut为true 也会影响核心线程)

long keepAliveTime = 10L; //非核心线程的存活时间

//第四个参数 指定第三个时间参数的单位

TimeUnit unit = TimeUnit.SECONDS;

//第五个参数 线程池中的任务队列.常用的有三种队列,

// 1、SynchronousQueue, 如果要运行的线程数大于核心线程数没有超过最大线程

// 则创建新的线程数来执行多余的线程,

// 即此处如果核心线程3 运行线程为6个,

// 最大线程10个 在该队列模式下 因为运行的线程数小于最大线程10 所以会开启新的3个线程来处理

//所以队列中没有未消费的线程

//BlockingQueue<Runnable> workQueue = new SynchronousQueue<Runnable>();



//2、LinkedBlockingQueue 如果要运行的线程数大于核心线程数没有超过最大线程

// 会将多于核心线程数的线程存放到队列中,

//所以的线程都通过核心线程消费

BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>();

//3、ArrayBlockingQueue 和LinkedBlockingDeque 一样 都可以用来存储多余的线程任务

// 区别在于ArrayBlockQueue可以设置容量大小,

// 而LinkedBlockingQueue 容量无限

//BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>();

ThreadPoolExecutor executor = new ThreadPoolExecutor

(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);





executor.execute(myRunnable);

executor.execute(myRunnable);

executor.execute(myRunnable);

System.out.println("---先开三个---");

System.out.println("核心线程数" + executor.getCorePoolSize());

System.out.println("线程池数" + executor.getPoolSize());

System.out.println("队列任务数" + executor.getQueue().size());



executor.execute(myRunnable);

executor.execute(myRunnable);

executor.execute(myRunnable);



System.out.println("---再开三个后的线程池中的情况---");

System.out.println("核心线程数" + executor.getCorePoolSize());

System.out.println("线程池数" + executor.getPoolSize());

System.out.println("队列任务数" + executor.getQueue().size());



try {

Thread.sleep(8000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("----8秒之后----");

System.out.println("核心线程数" + executor.getCorePoolSize());

System.out.println("线程池数" + executor.getPoolSize());

System.out.println("队列任务数" + executor.getQueue().size());







//超过核心线程的存活时间 本测试设置的为10s

//这里需要设置的休眠时间至少4秒以上 线程的运行时间为2秒

// 之前主线程暂停了8秒 去除执行线程的2秒(每个线程)

//则线程池中的非核心线程已经空闲了6秒 所以还需要空闲时间为4秒

// 这些线程池中的非核心线程就会被销毁

try {

Thread.sleep(4100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("----再过5秒之后----");

System.out.println("核心线程数" + executor.getCorePoolSize());

System.out.println("线程池数" + executor.getPoolSize());

System.out.println("队列任务数" + executor.getQueue().size());

}

3、当前任务线程数>最大线程数

3.1 使用LinkedBlockingQueue队列时(自动扩容,没有大小区分) 则会将超过核心线程的线程任务先放置到队列中,等到核心线程有空闲的时候会从队列中获取任务。

3.2 使用SynchronousQueue时队列 因为其中不存储缓存队列信息,所有当任务数大于最大线程数时,会报错(线程池中已经有了最大的线程数了,但是还是有其他线程没法执行)

3.3 ArrayBlockingQueue队列(限制其大小)

核心线程会消费与其数量相同的线程任务,而其他的线程任务会先放置到队列中,如果队列不足则会创建新的线程去消费剩余的任务线程。

如果创建线程数量和核心线程之要大于最大线程数量则会报错。例如 队列长度为1,有8个线程任务,3个核心线程消耗3个线程任务,将一个线程任务放置到队列中,此外还需要

创建4个线程去消耗剩余的线程任务,但是如果新建4个线程的话,线程池中总共会出现7个线程大于最大线程数,会报错。

@Test

public void testTwo(){





//当任务线程数大于最大线程数

// 使用LinkedBlockingQueue队列时(自动扩容,没有大小区分)

// 则会将超过核心线程的线程任务先放置到队列中,等到核心线程有空闲的时候会从队列中获取任务。

//BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();

//使用SynchronousQueue时队列 因为其中不存储缓存队列信息,所有当任务数大于最大线程数时,

// 会报错(线程池中已经有了最大的线程数了,但是还是有其他线程没法执行)



// ArrayBlockingQueue队列(限制其大小)

//核心线程会消费与其数量相同的线程任务,而其他的线程任务会先放置到队列中,

// 如果队列不足则会创建新的线程去消费剩余的任务线程。

// 如过创建的线程总数和总容量还是小于线程任务数,则会报错

//BlockingQueue<Runnable> queue = new SynchronousQueue<Runnable>();

//设置队列数大于最大任务线程数 模拟长度无限制

BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(3);

ThreadPoolExecutor executor = new ThreadPoolExecutor

(3, 6,

100, TimeUnit.SECONDS, queue);

System.out.println("核心线程数" + executor.getCorePoolSize());

System.out.println("线程池数" + executor.getPoolSize());

System.out.println("队列任务数" + executor.getQueue().size());



//执行线程

executor.execute(myRunnable);

executor.execute(myRunnable);

executor.execute(myRunnable);

executor.execute(myRunnable);

executor.execute(myRunnable);

executor.execute(myRunnable);

executor.execute(myRunnable);

executor.execute(myRunnable);

executor.execute(myRunnable);

executor.execute(myRunnable);







System.out.println("任务队列的大小:"+queue.size());

System.out.println("核心线程数" + executor.getCorePoolSize());

System.out.println("线程池数" + executor.getPoolSize());

System.out.println("队列任务数" + executor.getQueue().size());



try {

Thread.sleep(4000);

} catch (InterruptedException e) {

e.printStackTrace();

}





System.out.println("任务队列的大小:"+queue.size());

System.out.println("核心线程数" + executor.getCorePoolSize());

System.out.println("线程池数" + executor.getPoolSize());

System.out.println("队列任务数" + executor.getQueue().size());

}

* ThreadPoolExecutor的线程拒绝策略

* 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

handler有四个选择:

策略1、ThreadPoolExecutor.AbortPolicy()

抛出java.util.concurrent.RejectedExecutionException异常

策略2:ThreadPoolExecutor.CallerRunsPolicy

用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务(启动该线程的调用方法中);如果执行程序已关闭,则会丢弃该任务

策略3: DiscardOldestPolicy

从队列中删除一个线程任务,然后将剩余的线程任务添加进入线程池中

策略4:ThreadPoolExecutor.DiscardPolicy

用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务

 测试用例代码:

    public class ThreadPoolTest {

        private Runnable myRunnable = null;

        @Before
        public void initRunnable() {

            myRunnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                        System.out.println(Thread.currentThread().getName() + " run");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
        }

        @Test
        public void testOne() {
            //第一个参数:核心线程的个数
            int corePoolSize = 3;

            //第二个参数 最大线程个数
            int maximumPoolSize = 10;

            //第三个参数 线程的存活时间(一般是非核心线程的可存活时间, 但是如果设置了allowCoreThreadTimeOut为true 也会影响核心线程)
            long keepAliveTime = 100L;

            //第四个参数 指定第三个时间参数的单位
            TimeUnit unit = TimeUnit.SECONDS;

            //第五个参数 线程池中的任务队列.常用的有三种队列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue
            SynchronousQueue<Runnable> workQueue = new SynchronousQueue<Runnable>();

            ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);

            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);

            System.out.println("---先开三个---");
            System.out.println("核心线程数" + executor.getCorePoolSize());
            System.out.println("线程池数" + executor.getPoolSize());
            System.out.println("队列任务数" + executor.getQueue().size());
            /*
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            */

            System.out.println("---再开三个---");
            System.out.println("核心线程数" + executor.getCorePoolSize());
            System.out.println("线程池数" + executor.getPoolSize());
            System.out.println("队列任务数" + executor.getQueue().size());

            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();

            }

            System.out.println("----8秒之后----");
            System.out.println("核心线程数" + executor.getCorePoolSize());
            System.out.println("线程池数" + executor.getPoolSize());
            System.out.println("队列任务数" + executor.getQueue().size());

        }


        @Test
        public void testTwo() {
            //BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
            //BlockingQueue<Runnable> queue = new SynchronousQueue<Runnable>();
            BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(6); //设置队列数大于最大任务线程数 模拟长度无限制
            ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 100, TimeUnit.SECONDS, queue);

            System.out.println("核心线程数" + executor.getCorePoolSize());
            System.out.println("线程池数" + executor.getPoolSize());
            System.out.println("队列任务数" + executor.getQueue().size());

            //任务线程数6个

            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            System.out.println("任务队列的大小:" + queue.size());
            System.out.println("核心线程数" + executor.getCorePoolSize());
            System.out.println("线程池数" + executor.getPoolSize());
            System.out.println("队列任务数" + executor.getQueue().size());

            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();

            }
            System.out.println("任务队列的大小:" + queue.size());
            System.out.println("核心线程数" + executor.getCorePoolSize());
            System.out.println("线程池数" + executor.getPoolSize());
            System.out.println("队列任务数" + executor.getQueue().size());
        }

        @Test
        public void testThree() {
           //BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
           //BlockingQueue<Runnable> queue = new SynchronousQueue<Runnable>();
            BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(3);
            ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 100, TimeUnit.SECONDS, queue);

            System.out.println("核心线程数" + executor.getCorePoolSize());
            System.out.println("线程池数" + executor.getPoolSize());
            System.out.println("队列任务数" + executor.getQueue().size());

            //线程任务数9个
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);

            System.out.println("核心线程数" + executor.getCorePoolSize());
            System.out.println("线程池数" + executor.getPoolSize());
            System.out.println("队列任务数" + executor.getQueue().size());

            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务队列的大小:" + queue.size());
            System.out.println("核心线程数" + executor.getCorePoolSize());
            System.out.println("线程池数" + executor.getPoolSize());
            System.out.println("队列任务数" + executor.getQueue().size());
        }
    }

二、线程池的停止操作

1、shutdown()方法

阻止新来的任务提交,对已经提交了的任务不会产生任何影响。当已经提交的任务执行完

后,它会将那些闲置的线程(idleWorks)进行中断,这个过程是异步的。

具体的流程是:

通过将线程池的状态改成SHUTDOWN,当再将执行execute提交任务时,如果测试到状态不为RUNNING,则抛出rejectedExecution,从而达到阻止新任务提交的目的

2、shutdownNow()方法

阻止新来的任务提交,同时会中断当前正在运行的线程,即workers中的线程。另外它还将workQueue中的任务给移除,并将这些任务添加到列表中进行返回。通过将线程池的状态改成STOP,当再将执行execute提交任务时,如果测试到状态不为RUNNING,则抛出rejectedExecution,从而达到阻止新任务提交的目的。

3、awaitTermination(long timeout,TimeUnit unit)

awaitTermination会一直等待,直到线程池状态为TERMINATED或者,等待的时间到达了指定的时间。

三、Executors工具类获取线程池

          在阿里开发手册中当我们使用Executors创建线程池不被推荐,反而建议我们使用ThreadPoolExecutor来创建 这个分析一下原因

 @Test
    public void testNewFixedPool() throws InterruptedException {
        /**
         * newCachedThreadPool 创建一个可缓存的线程池工厂,对于新的任务,先去查看当前的缓存池中是否有空闲的线程
         * 如果有则直接使用线程,否则新建一个线程,线程池中空闲线程闲置超过60秒未被使用则该线程被移除销毁(本质上不会一直保留线程)
         * 其实内部使用的new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
         * 结合上面所述,则可以明白 创建的线程工厂 核心线程为0 可允许的最大线程为Integer.MAX_VALUE(如果使用Executors 默认最大线程 在极端情况下
         * 会特别的大 导致OOM 所有阿里开发手册不建议使用)
         */
        //ExecutorService executorService = Executors.newCachedThreadPool();

        /**
         * newFixedThreadPool 创建一个有固定线程数的线程池,对于新的任务,如果固定线程中的所有线程有空闲线程,则使用空闲线程执行任务
         * 没有则放入LinkedBlockingQueue 的基于链表的队列中,但是创建的队列是无限大的,可能会造成队列中的任务无限大,造成内存溢出
         * 其内部是使用new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
         * 核心线程和最大线程是一致且固定,线程池中始终保持固定的线程数量
         */
        //executorService = Executors.newFixedThreadPool(10);


        /**
         * newScheduledThreadPool创建一个可以周期性执行线程的线程池服务,该类底层调用
         * new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
         * 所以会出现线程池中线程无限大 导致OOM(内存溢出)
         */

        CountDownLatch  countDownLatch = new CountDownLatch(1);
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

        scheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("delay 3 seconds");
                countDownLatch.countDown();
            }
        }, 3, TimeUnit.SECONDS);

        countDownLatch.await();


        /**
         * 单线程化的线程池 即底层使用  (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
         * 核心线程以及最大线程都是1 多余的任务则使用队列来保存,保证每次请求都使用一个线程
         * (同newFixedThreadPool 极端情况队列中保存大量任务 导致OOM)
         */
        Executors.newSingleThreadExecutor();

        /**
         * 单线程化的定时执行的线程池 类似于Executors.newScheduledThreadPool(1);
         * 当然可能存在的问题与newScheduledThreadPool的问题一致
         */
        Executors.newSingleThreadScheduledExecutor();

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值