线程池快速使用(二)

为什么不使用Executors创建线程池

使用Executors线程池创建线程会有资源耗尽的风险

  • 创建FixedThreadPool 或者 SingleThreadPool 时 , 默认创建的请求队列的长度为Integer.MAX_VALUE , 可能会导致请求堆积 , 造成内存溢出
  • 创建CachedThreadPool , 默认最大线程数量是Integer.MAX_VALUE , 可能会导致大量线程的创建 , 造成内存溢出
  • 综上 , 为了防止资源耗尽 , 不推荐使用Executors创建线程池

线程池的工作流程

  1. 判断当前线程数是否大于核心线程数 , 如果不大于 , 则创建新线程执行任务
  2. 如果当前线程数大于核心线程数 , 则判断当前任务队列是否已经满了 , 如果没满 , 就将任务提交给任务队列
  3. 如果任务队列满了 , 则判断当前线程数是否大于最大线程数 , 如果不大于 , 就创建临时线程执行该任务
  4. 如果当前线程数大于最大线程数 , 那么执行线程池的拒绝策略

线程池的状态

RUNNING:运行状态 . 线程池创建之后就处于RUNNING状态 , 此时可以接收/处理新任务 ,
SHUTDOWN:关闭状态 . 调用shutdow方法后就处于SHUTDOWN状态 , 不能接收新任务可以处理未完成任务
STOP:阻断状态 . 调用shutdownNow方法后处于STOP状态 , 不能接收新任务 , 不会完成未完成任务 , 同时会将正在进行的任务终止
TIDYING:整理状态 . 所有任务完成时 , 就会变为TIDYING状态 . 处于当前状态的线程池会调用terminate()方法
TERMINATED:终止状态 . 在 terminated() 方法执行完后进入该状态

在这里插入图片描述

线程池的关闭方法

  • shutdown( ) 关闭当前线程池 , 调用该方法之后 , 线程池不再接收新的任务 , 但是会将没有执行完的任务继续执行
  • shutdownNow( ) 立即关闭线程池 , 调用该方法后 , 线程池不再接收新的任务 , 同时没有完成的任务也不会继续执行 , 而是将没有完成的任务作为返回值返回
  • isShutdown( ) 判断线程池是否关闭 , true 关闭 false 没关闭
  • isTerminated( ) 判断线程池是否终止 , true 关闭 false 没关闭

问题1 : shutdown和shutdownNow的区别

  • shutdownNow会首先将线程池的状态设置为STOP , 然后尝试停止所有正在执行任务或者没有执行任务的线程 (所有任务线程都停止 , 不管有没有在执行任务) ;
  • shutdown会将线程池的状态设置为SHUTDOWN , 然后尝试停止所有没有执行任务的线程 ( 停止没有执行任务的线程 , 执行任务的线程不停止 ) ;
  • 总结 : 所有shutdown会将没执行的任务执行完 , shutdownNow则是立即停止 , 没完成的任务也不执行了 .

问题2 : 调用shutdown后线程池会终止吗 ?

调用shutdown后 , 线程池不会立即终止 . 在关闭线程池之后 , 只有将没有完成的任务完成后 , 再对线程池申请的资源进行回收之后 , 线程池才会终结 (terminated)

怎么判断线程池任务执行完毕

  1. 判断线程池是否处于终结状态
    优点 : 实现简单
    缺点 : 需要关闭线程池 , 线程池状态才会变为terminated
    public static void main4(String[] args) {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
                0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
        addTask(threadPool);
        if (isComplete(threadPool)) {
            System.out.println("执行结束");
        }
    }
	// 往线程池当中添加任务
	public static void addTask(ExecutorService executorService) {
	    for (int i = 1; i <= taskSize; i++) {
	        final int runTaskSize = i;
	        executorService.submit(()->{
	            try {
	                System.out.println("第" + runTaskSize + "个任务正在执行" );
	                Thread.sleep(1000);
	            } catch (InterruptedException e) {
	                throw new RuntimeException(e);
	            }
	        });
	    }
	}
	// 判断线程池任务是否执行结束
	public static boolean isComplete (ThreadPoolExecutor threadPoolExecutor) {
	    // 判断线程池状态是否为终止状态
	    threadPoolExecutor.shutdown();
	    while (!threadPoolExecutor.isTerminated()) {
	    }
	    return true;
	}
  1. 判断线程池任务数量和完成任务数量是否一致
    优点 : 无需关闭线程池
    缺点 : 由于线程池任务数量和完成任务数量是一直变化的 , 因此得到的只是近似值
    public static void main4(String[] args) {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
                0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
        addTask(threadPool);
        if (isComplete(threadPool)) {
            System.out.println("执行结束");
        }
    }
	// 往线程池当中添加任务
	public static void addTask(ExecutorService executorService) {
	    for (int i = 1; i <= taskSize; i++) {
	        final int runTaskSize = i;
	        executorService.submit(()->{
	            try {
	                System.out.println("第" + runTaskSize + "个任务正在执行" );
	                Thread.sleep(1000);
	            } catch (InterruptedException e) {
	                throw new RuntimeException(e);
	            }
	        });
	    }
	}
	// 判断线程池任务是否执行结束
	public static boolean isComplete (ThreadPoolExecutor threadPoolExecutor) {
        // 判断线程池任务数量和完成任务数量是否一致
        while (threadPoolExecutor.getTaskCount() != threadPoolExecutor.getCompletedTaskCount()) {
        }
        return true;
	}
  1. 使用CountDownLatch计数器

原理 : 在底层创建一个计数器 , 计数器的初始值就是传入的任务数量 , 每次调用CountDown就让计数器减少1 , 当计数器的值为0之后就唤醒等待的线程

public static void addTaskCountLauch(ExecutorService executorService) throws InterruptedException {
	// 创建计数器并指定任务数量
    CountDownLatch countDownLatch = new CountDownLatch(taskSize);
    for (int i = 1; i <= taskSize; i++) {
        final int runTaskSize = i;
        executorService.submit(()->{
            try {
                boolean shutdown1 = executorService.isShutdown();
                System.out.println("第" + runTaskSize + "个任务正在执行" + "线程池状态" + shutdown1);
                Thread.sleep(1000);
                // 任务完成就减1
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
    }
    // 当所有任务完成后就唤醒当前线程
    countDownLatch.await();
    System.out.println("执行结束");
}
  • 55
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值