java 线程池

好文:

https://www.jianshu.com/p/76ddf9745c43

https://blog.csdn.net/u011479540/article/details/51867886

 

线程池优雅平滑关闭

附上一个平缓关闭池的方法(摘自rocketmq ThreadUtil)

/**
     * 优雅关闭线程池,以标准输出流的方式输出提示信息
     * @param executor 需要关闭的线程池
     * @param timeout 最大等待时间(毫秒),超时则直接调用 executor 的 shutdownNow() 方法强行关闭
     * @param name 线程池名称
     * @param printRate 打印的时间间隔
     */
    public static void shutdownGracefully(ExecutorService executor, long timeout, String name, long printRate) {
        long start = System.currentTimeMillis();
        executor.shutdown();
        try {
            // 阻塞等待线程池中任务都完成了才结束
            while (!executor.awaitTermination(printRate, TimeUnit.MILLISECONDS)) {
                System.out.println(String.format("[%s] WARN %s Shuting down %s", DateUtil.format(FormatType.DATE_TIME_MS), name, executor));
                if (System.currentTimeMillis() - start >= timeout) {
                    System.out.println(String.format("[%s] WARN %s shutdown timeout[%s], force terminate! %s", 
                            DateUtil.format(FormatType.DATE_TIME_MS), name, timeout, executor));
                    executor.shutdownNow();
                    break;
                }
            }
        } catch (InterruptedException e) {
            System.out.println(String.format("[%s] WARN catch InterruptedException and shutdown %s now!", DateUtil.getCurrentDate(), name));
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
        System.out.println(String.format("[%s] INFO shutdown %s, elapse time %s", 
                DateUtil.format(FormatType.DATE_TIME_MS), name, System.currentTimeMillis() - start));
    }

 

题外话:定时任务,可以用线程池“ScheduleThreadPoolExcutor”实现。

前提:如果并发量比较小,只是要求在子线程执行部分业务,那可以考虑使用

Executors.newSingleThreadExecutor()

 

java通过Executors提供四种线程池,分别为:

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 

newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 

newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 

newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFOLIFO, 优先级)执行。

 

线程池不建议使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方
式让写的代码用户更加明确线程池的运行规则,养成规避资源耗尽的风险习惯。
说明: Executors 返回的线程池对象的弊端如下:
a) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
b) CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM
 

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4,20,600,TimeUnit.SECONDS,
                       new LinkedBlockingQueue<Runnable>(50),new ThreadPoolExecutor.DiscardOldestPolicy()));

参数含义:(核心线程,最大线程,线程存活时间,时间单位,阻塞队列【不要用默认参数,默认为Interger.MAX_VALUE】,任务多出队列后遗弃线程的策略)

下面是一个example:

/**
 * @ClassName FirstController
 * @Description 测试
 * @Author lkun
 **/
@ApiIgnore
@RestController
public class FirstController {

    private final static Logger logger = LoggerFactory.getLogger(FirstController.class);

    private ExecutorService threadPool;

    {
        //threadPool = Executors.newSingleThreadExecutor();
        threadPool = new ThreadPoolExecutor(2, 20, 10,
                TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(900),new ThreadPoolExecutor.DiscardOldestPolicy());
    }

    @Autowired
    IrisDataService irisDataService;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    IrisDataRepository irisDataRepository;

    @RequestMapping(value = "/helloWorld",method = RequestMethod.GET)
    public String helloWorld() throws Exception {
        for(int i = 0 ; i < 1000; i++) {
            final int num = i;
            threadPool.execute(new Runnable(){
                @Override
                public void run(){
                    try {
                        Thread.sleep(2000);
                        System.out.println("test : " + num);
                    }catch(Exception e){
                        logger.error("error thread:" + e.getMessage(),e);
                    }
                }
            });
        }
        System.out.println("Thread run end!");
        return "true";
    }

    @RequestMapping(value = "/testMethod",method = RequestMethod.GET)
    public String testMethod() {
        logger.info("test info");
        logger.debug("test debug");
        return "test success11ger";
    }
}

上述代码,由于循环1000次速度极快,但线程池只允许阻塞队列900个,那么真实情况会如何执行?

A:循环一共被执行920次,20是最大线程数,即拿到执行权的线程;900是其他拿不到执行权排队中的线程,其余的80条就被遗弃策略给丢掉了....

 

workQueue(工作队列):用于保存等待执行的任务的阻塞队列。

  1. ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO(先进先出)原则对任务进行排序。使用该队列,线程池中能创建的最大线程数为maximumPoolSize。

  2. LinkedBlockingQueue:基于链表结构的无界阻塞队列,按FIFO(先进先出)原则对任务进行排序,吞吐量高于ArrayBlockingQueue。使用该队列,线程池中能创建的最大线程数为corePoolSize。静态工厂方法 Executor.newFixedThreadPool()使用了这个队列。

  3. SynchronousQueue:一个不存储元素的阻塞队列。添加任务的操作必须等到另一个线程的移除操作,否则添加操作一直处于阻塞状态。静态工厂方法 Executor.newCachedThreadPool()使用了这个队列。

  4. PriorityBlokingQueue:一个支持优先级的无界阻塞队列。使用该队列,线程池中能创建的最大线程数为corePoolSize。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值