JAVA线程池问题

来源:互联网 发布:ubuntu 挂载 编辑:程序博客网 时间:2024/06/06 21:39


    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的值

   

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 专升本差了一分怎么办 登录不上学信网怎么办 steam被好友删了怎么办 护士继续教育学分证丢了怎么办 护士证到期未延续注册怎么办 学籍和户口不在一起小升初怎么办 定了酒店不能退怎么办 去哪儿网酒店不允许取消怎么办 快递寄送身份证扣海关怎么办 7岁龋齿烂到牙根怎么办 法院判完对方说没钱怎么办 初中填完志愿后怎么办 上海小学借读一年级没有学籍怎么办 学历不高的我该怎么办 没学历的我该怎么办 物业达不到服务标准该怎么办 没有能力的人该怎么办 工作累了腰疼怎么办 机场来早了6小时怎么办 苏宁金融综合评分不足怎么办 苏宁金融秒拒怎么办 微盘账号忘记了怎么办 天府e税忘记密码怎么办 未成年在外面没地方住怎么办? 半框眼镜片掉了怎么办 选修差0.5个学分怎么办 脱产考博社保卡怎么办 幼儿上课不认真听讲怎么办 手机恢复的音频文件打不开怎么办 高考志愿填报不记得密码怎么办 经济纠纷案被告没有证据怎么办 管家婆管理员密码忘记了怎么办 人离职了公司扣发工资怎么办? 美国给我们断网怎么办 sci发表后发现错误怎么办 pos机按键是英文怎么办 蔚县县医院慢病本怎么办 知网下载的论文乱码怎么办 被期刊网骗了怎么办? 缝针缝到神经上怎么办 单位有个事特别多的领导怎么办