ThreadPoolExecutor 源码分析

我主要从以下几个部分来剖析一下ThreadPoolExecutor线程池的实现原理

1. 先拿个例子来讲解

2. 分析内部实现

3. 总结


1. 先拿个例子来讲解

我感觉看懂整个实现,最主要还是看懂整个执行过程,这样就不怕中间哪一步不懂了,举个例子吧

		final int CORE_POOL_SIZE = 1;//核心线程数,后边解释这个变量
		final int MAX_POOL_SIZE = 3;//最大允许的任务数,超过这个数目的话,就reject
		ThreadFactory factory = Executors.defaultThreadFactory();//默认的线程产生工厂
		ExecutorService exec = new CustomExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE,
				5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1),
				factory);

		exec.execute(new Runnable() {
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}
		});

		exec.shutdown();
		try {
			while (!exec.awaitTermination(1, TimeUnit.SECONDS));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace(System.out);
		}


这里的CustomExecutor是我复制了ThreadPoolExecutor的源代码,主要是方便调试跟踪,因为我发现我把jdk的源代码attach上去以后,在调试跟踪的时候,居然代码和调试的位置不对应。呐,调试代码最要紧的就是开心啦,调试很麻烦,这个大家都是不想的啦,TVB热语,大家不要扔鸡蛋,--^_^--
构造函数参数有这样5个,核心线程数,最大允许的任务数(说最大线程池数,我觉得不妥吧,如果有不对,请指教),时间,时间单位,队列(在当前处理的任务超过了CORE_POOL_SIZE的时候,任务会放进队列里去),产生线程的工厂。我这里设置的CORE_POOL_SIZE为1,MAX_POOL_SIZE为3,队列的长度为1,就是为了测试放入执行三个任务后是第三个任务会怎么执行的.如果在第一个任务没有执行完毕,即在小于CORE_POOL_SIZE的情况下,会调用addIfUnderCorePoolSize(command),什么意思呢?产生了一个工作线程来执行这个任务command呗,放入第二个任务会进去到队列workQueue.offer(command),因为CORE_POOL_SIZE为1,所以不能再执行任务了,只能将新来的任务暂存在队列中,再来一个任务---第三个任务则会调用addIfUnderMaximunPoolSize(command),这又是什么意思?就是说队列也满了,因为我们设置队列长度为1,所以就调用该函数,来创建一个新的工作线程来处理这个无法进入队列的任务,代码参考下面。


    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
            if (runState == RUNNING && workQueue.offer(command)) {
                if (runState != RUNNING || poolSize == 0)
                    ensureQueuedTaskHandled(command);
            }
            else if (!addIfUnderMaximumPoolSize(command))
                reject(command); // is shutdown or saturated
        }

好了,下面再详细讨论一下CORE_POOL_SIZE,MAX_POOL_SIZE,keepAliveTime,因为我觉得这个还是线程池好像都是围绕这几个变量打造的。


2. 分析内部实现

1. 当前的任务数小于corePoolSize
    每新来一个任务,则会创建一个工作线程(worker)来处理当前的任务,worker在keepAliveTime的时间内

如果没有收到新的任务,则该worker会消失
这里比较玄妙的地方在以下的code:

  private Thread addThread(Runnable firstTask) {
		Worker w = new Worker(firstTask);
		Thread t = threadFactory.newThread(w);
		if (t != null) {
			w.thread = t;
			workers.add(w);
			int nt = ++poolSize;
			if (nt > largestPoolSize)
				largestPoolSize = nt;
		}
		return t;
	}

  public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
每次新来一个任务在小于corePoolSize或者队列放不下的时候都会调用addThread,然后产生一个worker,新来的任务会作为worker的第一个任务,线程工厂封装这个worker,返回一个新的线程,这里的addThread返回来一个线程,再执行start调用worker的run函数。

2. 当前的任务数大于等于corePoolSize
             a. 放入到queue中,如果有工作线程空闲,即task为null时候,从队列中拿一个任务getTask(), r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);

public void run() {
			try {
				System.out.println(Thread.currentThread().getName());
				Runnable task = firstTask;
				firstTask = null;
				while (task != null || (task = getTask()) != null) {
					runTask(task);
					task = null;
				}
			} finally {
				workerDone(this);
			}
		}
	Runnable getTask() {
		for (;;) {
			try {
				int state = runState;
				if (state > SHUTDOWN)
					return null;
				Runnable r;
				if (state == SHUTDOWN) // Help drain queue
					r = workQueue.poll();
				else if (poolSize > corePoolSize || allowCoreThreadTimeOut)
					r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
				else
					r = workQueue.take();
				if (r != null)
					return r;
				if (workerCanExit()) {
					if (runState >= SHUTDOWN) // Wake up others
						interruptIdleWorkers();
					return null;
				}
				// Else retry
			} catch (InterruptedException ie) {
				// On interruption, re-check runState
			}
		}
	}
b. 当队列已经满的时候,则判断maxPoolSize是否小于当前的处理任务数以及等待的任务数注意,如果队列已经满的时候,则再来线程的话会先于队列中的任务而得到执行
private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {
		Thread t = null;
		final ReentrantLock mainLock = this.mainLock;
		mainLock.lock();
		try {
			if (poolSize < maximumPoolSize && runState == RUNNING)
				t = addThread(firstTask);
		} finally {
			mainLock.unlock();
		}
		if (t == null)
			return false;
//执行会先于队列中的任务,然后这个新的工作线程(工作线程会随着新任务而产生,见addThread)执
//行完当前的任务以后,会去取队列中的线程
		t.start();
		return true;
	}


3. 总结

分析ThreadPoolExecutor主要原因还是因为我想研究一下MINA2.0中的那个OrderedThreadPoolExecutor线程池的问题,这个类就是继承了ThreadPoolExecutor来实现的,分析OrderedThreadPoolExecutor有助于了解到MINA是怎么处理session的IO的事件的,它的处理还是比较高效的,不仅仅是利用利用java NIO打造的IOService,它的层次还是比较清晰,值得学习一下的。


欢迎关注我的微博http://weibo.com/stuckin


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值