java-源码解读-线程池实现原理-1

为什么要使用线程池

合理利用线程池能够带来三个好处。(复制过来的,懒)
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池的使用

先定义线程池:
方法一:

private ExecutorService initExecutor(int size){
        return Executors.newFixedThreadPool(size);//就是调用Executors工具类构造
}

方法二:

//自己构造
private ThreadPoolExecutor initExecutor2(int corePoreSize, int maxPoolSize, int queue, final String threadName){
    return new ThreadPoolExecutor(corePoreSize,
            maxPoolSize, 1,TimeUnit.MINUTES,
            new ArrayBlockingQueue<Runnable>(queue),
            new ThreadFactory(){
                AtomicInteger i=new AtomicInteger(0);
                    @Override
                    public Thread newThread(Runnable r) {
                        return new Thread(threadName+i.incrementAndGet());
                    }
                }, new ThreadPoolExecutor.CallerRunsPolicy()
            );
}

然后再向线程池中提交任务:

ExecutorService.submit(..);

ThreadPoolExecutor.execute(..);

submit 方法和execute方法提交任务有什么区别呢?请参考 java-源码解读-线程池提交之execute和submit有何不同

通过这两种方法定义的线程池哪种更好呢,先来了解线程的实现原理,再回答这个问题。

线程池的实现原理

不管以何种方法创建线程池,最终的结果都是调用ThreadPoolExecutor的构造方法:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
                             //...
                             }

创建一个线程池需要输入几个参数:

corePoolSize:当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。

workQueue(任务队列):用于保存等待执行的任务的阻塞队列。
maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字,Debug和定位问题时非常又帮助。

 RejectedExecutionHandler(拒绝策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。 

        AbortPolicy:直接抛出异常。

        CallerRunsPolicy:只用调用者所在线程来运行任务。

        DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。

        DiscardPolicy:不处理,丢弃掉。

keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
TimeUnit(线程活动保持时间的单位)。
上面的参数,在下面会提到。

当向线程池提交任务之后,到底发生了什么?

不管是用execute方法提交任务,还是用submit提交任务,最后都是调用ThreadPoolExecutor的execute方法。

   public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        /**
         *工作线程数小于corePoolSize,线程池会创建一个线程来执行任务
         * addWorker就是创建一个新的工作线程
        */
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        /**
         *如果工作线程数大于corePoolSize
         *就把任务加入到workQueue中
         */
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
               //recheck。有可能把任务加入到队列时,线程池已不是RUNNING状态
             if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        /**
        *如果不能加入workQueue
        *只有执行拒绝策略了
        */
        else if (!addWorker(command, false))
            reject(command);
    }

到目前为止说完了任务担交的过程。

提交的任务是什么时候执行的呢?

任务的执行在Wordker线程中执行, Worker 是ThreadPoolExecutor的内部类。
Worker实现了Runnable接口,也是一个线程。
直接看Worker的run方法执行了什么逻辑.

  public void run() {
    runWorker(this);
  }
  final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock();
    boolean completedAbruptly = true;
    try {
        /**
         *worker取任务
         *worker首先执行task,即通过worker构造器传递给worker的task
         * 如果task为空,就调用getTask()方法从队列中取任务
        */
        while (task != null || (task = getTask()) != null) {
            w.lock();              
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                //任务执行前,回调方法
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    /**
                    *此处就是任务执行的地方
                    */
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    //任务执行后,回调方法
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

这就是一个任务的生命周期,虽然省略ThreadPoolExecutor的好多源码,但大体可以看出一个任务的生命周期。

怎样构造一个安全的线程池

回答上面的问答,怎样构造一个线程比较好呢?

要构造一个好的线程池,无非就是控投制几个参数,corePoreSize ,maxPoolSize,queueSize,queue,RejectedExecutionHandler,和ThreadFactory。
corePoreSize ,maxPoolSize主要根据业务类型和cpu数量来决定,这里不讨论。总之耗cpu的任务就少开几个线程,io多的任务就多开个线程。
queueSize:列队的长度,我个人认为这个参数很重要,ThreadPoolExecutor自带的几个线程池实现比   如:newFixedThreadPool,newSingleThreadExecutor默认使用的都是无界队列,这个时候maxPoolSize,RejectedExecutionHandler这两个参数将不起使用。因为队列是无界,永远不会拒绝任务。而且当任务非常多时有内存溢出的风险。
queue:生产者消费者队列。不同的队列可以实现不同的功能。比如:newFixedThreadPool,newSingleThreadExecutor都使用LinkedBlockingQueue实现,newCachedThreadPool使用SynchronousQueue实现。而DelayQueue是java ScheduledExecutorService 的实现基础。
RejectedExecutionHandler:主要根据业务决定。
根据以上几个参数的分析,我个人认为少用java自带的线程池,尽量构造符合业务需要的线程池。
ThreadFactory:线程的工厂。当代码有异常时,此时ThreadFactory就派上用场了,我们可以给线程起一个有意义的名字,这样就能在异常中识别哪个线程出现了问题。

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值