JAVA线程池问题


    JDK本身已经提供了完整的线程池实现,因此在使用JAVA中使用线程池是很轻松方便的。

   查看线程池的构造器

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }


其中 corePoolSize表示默认的线程数量,maximumPoolSize 表示最大的线程数量,workQueue 代表执行任务的等待队列。keepAliveTime表示线程的保持时间,如果线程数大于corePoolSize时,如果线程在keepAliveTime时间内没有获取到执行任务,就将线程销毁掉,保持线程池中只有corePoolSize个线程。 threadFactory线程工厂,handler,当线程池满时,采用的处理策略。

   查看

     public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

  可以发现线程池的使用会经历一下几个阶段。

  1, 线程池刚启动,此时线程池中没有线程。

  2,一个执行任务添加到线程池中。线程池发现当前线程数小于corePoolSize,新加一个worker线程,执行任务。

  3,继续向线程池中添加执行任务,如果线程池的线程数量已经等于corePoolSize,将执行任务添加到队列中,如果加入队列失败,将增加一个worker线程执行任务。

 4, 如果步骤3中,worker线程数量已经等于maximumPoolSize,执行handler接口定义的方法,代表线程池已满。


  上面的过程会有一个很蛋疼的问题,就是只有线程队列满了之后,才会启用新的线程。加入初始设定corePoolSize=1,maximumPoolSize=150,queue采用默认长度Integer.MAX_VALUE。那么执行结果会是怎么样呢,那就是在排队任务必须等于Integer.MAX_VALUE时,才会启动第二个线程,直到150个线程。可以认为,只开通一个售票窗口,直到排队的人塞满之后,才会开动第二个窗口。

  换一个思路,能不能发现有人在排队的时候,就将所有的窗口打开,这样,大多数任务都不用长时间排队的问题。按照这种思路,实现一种动态分配线程数的策略,使有任务排队时实际线程数在corePoolSize ~ maximumPoolSize 之间移动。

    实现代码如下:

   import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * @author liaokucheng@ftown.com
 * @date 2016年11月10日
 * 
 */
public class DynamicThreadPool extends ThreadPoolExecutor {


private volatile int corePoolSize;


private volatile int maxPoolSize;


private volatile int poolSize;


/**
* @param corePoolSize
* @param maximumPoolSize
* @param keepAliveTime
* @param unit
* @param workQueue
*/
public DynamicThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
this.corePoolSize = corePoolSize;
this.maxPoolSize = maximumPoolSize;
this.poolSize = corePoolSize;
}
/**
* 改变核心线程数
*/
private void changeCoorePoolSize(int i) {
if (i > 0) {// 增加
if (poolSize < maxPoolSize) {
poolSize += i;
this.setCorePoolSize(poolSize < maxPoolSize ? poolSize : maxPoolSize);
startMonitor();// 增加线程数时,需要启动监控线程,由监控线程来控制空闲时,线程数的减少
}
} else {// 减少,减少时,不需要启动监控线程
if (poolSize > corePoolSize) {
poolSize += i;
this.setCorePoolSize(poolSize > corePoolSize ? poolSize : corePoolSize);
}
}


}


/**
* 动态增长
*/
private void dynamicChange() {
if (poolSize < maxPoolSize) {
if (this.getTaskCount() - this.getCompletedTaskCount() > poolSize) {// 任务数量超过最小线程数量,有任务增正在排队
changeCoorePoolSize(1);
}
}


}


@Override
public void execute(Runnable command) {
dynamicChange();
super.execute(command);
}


/**
* 监控线程,使用监控线程来控制最小线程数

* @author liaokucheng@ftown.com
* @date 2016年11月10日

*/
protected class MonitorThread extends Thread {
/**
* 1分钟
*/
private volatile int sleepTime = 60000;


public void run() {
while (true) {
//如果线程池已经被关闭,那么结束监控线程线程
if (isShutdown()) {
return;
}
execute();
}
}


void execute() {
try {
Thread.sleep(sleepTime);
if (poolSize > corePoolSize) {
//未完成的任务数小于线程数,则认为线程有空闲,应该减少线程数
if (getTaskCount() - getCompletedTaskCount() < poolSize) {
changeCoorePoolSize(-1);
return;
}
}
} catch (InterruptedException e) {
// TODO
}
}
}


private volatile MonitorThread monitor;


/**
* 启动监控线程,使用double-check来保证监控线程的单例
*/
private void startMonitor() {
if (null == monitor) {
synchronized (this) {
if (null == monitor) {
monitor = new MonitorThread();
monitor.setDaemon(true);
monitor.start();
}
}
}


}
}


 添加任务时,判断未执行完成的任务是否大于当前线程数,如果大于,增加corePoolSize,同时,启动一个监控线程,监控线程用于线程池空闲时,缩小corePoolSize的值

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值