如何写好一个线程池(java)

在应用中,一个线程池工具类,需要些什么,我整理了如下几点:

  • 单例,而且是线程安全的单例
  • 线程异常处理,当线程在实际执行过程中,如果出现异常,起码得记个log吧
  • 线程池的设置,核心线程大小,阻塞队列,最大线程大小
/**
 * 线程池工具类
 */
public class ThreadPoolUtils {
    //核心线程数
    private static final int CORE_POOL_SIZE = 5;
    //最大线程数
    private static final int MAX_POOL_ZISE = 200;
    //存活时间
    private static final Long KEEP_ALIVE_TIME = 60L;

    //登记式单例
    private static class ThreadPoolHolder {
        private static final ThreadPoolExecutor executor =  
                new ThreadPoolExecutor( CORE_POOL_SIZE, 
                                        MAX_POOL_ZISE, 
                                        KEEP_ALIVE_TIME,
                                        TimeUnit.SECONDS, 
                                        new SynchronousQueue<Runnable>());
    }

    public static ExecutorService getExecutorService(){
        return ThreadPoolHolder.executor;
    }

    /**
     * 执行task
     */
    public static void execute(Runnable task){
        ThreadPoolHolder.executor.execute(getExcpThread(task));
    }

    public static void execute(Runnable runnable,Thread.UncaughtExceptionHandler excpHandler){
        Thread thread = getExcpThread(runnable);
        if(excpHandler != null)
            thread.setUncaughtExceptionHandler(excpHandler);

        ThreadPoolHolder.executor.execute(thread);
    }

    /**
     * 默认线程异常处理器
     */
    static class ThreadUncaughtException implements Thread.UncaughtExceptionHandler{

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            //log.error....
        }
    }

    /**
     * 获取带有异常处理的线程
     */
    public static Thread getExcpThread(Runnable task){
        Thread taskThread = null;
        if(task instanceof Thread)
            taskThread = (Thread)task;
        else
            taskThread = new Thread(task);

        if(taskThread.getUncaughtExceptionHandler() == null)
            taskThread.setUncaughtExceptionHandler(new ThreadUncaughtException());
        return taskThread;
    }

}
  • 对于单例,这里采用登记式单例,jvm自有的线程安全,而且也是懒加载。当然double check + volatile也可。
  • ThreadUncaughtException 默认的异常处理,只里面可以记一些log,报警,或者别的,根据业务而定
  • getExcpThread(),一方面,如果线程没有设置异常处理器,则设置一个默认的,如果已经存在,则使用自己设置的。
  • 创建线程的时候,有个Executors的静态类可以使用,用它可以快速创建一个线程池。但是建议在实际应用中尽量不要用。
    比如:Executors.newCachedThreadPool();看一下源码,它是这么实现的。
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());

他的最大线程数为Integer.MAX_VALUE,这个是很恐怖的,而且他采用的是同步队列,如果使用这个线程池的人,不清楚他的问题,随意的添加任务,当业务繁忙的时候很容易出问题。

再看Executors.newFixedThreadPool(),固定大小的线程池,实现源码如下:

new ThreadPoolExecutor(nThreads, nThreads, 0L,
                       TimeUnit.MILLISECONDS, 
                       new LinkedBlockingQueue<Runnable>());

这个采用的是无界队列,虽然他的最大线程数和核心线程数都设置为nThreads,但是,最大线程数其实没用,因为核心线程数用完之后,task就会进入阻塞队列,而这个队列是无界的。所以对于固定大小的线程池,如何去设置这个nThreads,很关键,太大了,空闲的时候,可能会有很多空闲的线程。太小了,可能会有很多任务在阻塞队列中,而且这个队列可以一直添加task。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值