线程池的使用
构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize:线程池维护线程的最少数量
- maximumPoolSize:线程池维护线程的最大数量
- keepAliveTime:线程池维护线程所允许的空闲时间
- unit:线程池维护线程所信息的空闲时间的单位
- workQueue:线程池所使用的缓冲队列
- threadFactory:线程创建工厂类
- handler:线程池对任务的处理策略
注意
- 线程池在创建的时候是不创建线程的,只有加入任务才创建线程,当然可以调用prestartCoreThread方法(是在ThreadPoolExecutor类中实现的)来初始化一个线程。
- 线程池先会创建完corePoolSize个线程,在把任务保存到队列中,队列满后才会再创建线程,直到规定的maximumPoolSize,然后会执行拒绝策略。
- 设置线程池的大小参考
- 如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1
- 如果是IO密集型任务,参考值可以设置为2*NCPU
BlockingQueue常用的3个实现类
- ArrayBlockingQueue 构造参数必须带一个int参数来指明其大小,FIFO
- LinkedBlockingQuene 大小不定,可以人为指定,若没有有Integer.MAX_VALUE来决定。FIFO
- SynchronousQueue 对其操作必须放和取交替完成。在线程池中它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务
LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量很大时其性能的可预见性低于ArrayBlockingQueue.
SynchronousQueue初始化后必须先有其他线程对其使用take()方法后才能使用put() 或者 offer()方法
java 定义好的4中类型的线程池
- newCachedThreadPool创建一个可缓冲线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可用线程,则新建线程
- newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- newScheduledThreadPool 创建一个定长线程池,支持定时和周期性任务执行。
- newSingleThreadExecutor 一个单线程化的线程池,确保所有任务按照指定的顺序执行。
下面是线程池核心的执行代码,只看懂一点:cry:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//它在执行完一个线程会getTask再去取一个,直到没有任务。
while (task != null || (task = getTask()) != null) {
w.lock();
//.......删除N行代码
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);
}
}
gettask方法
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
//......省略N行代码
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
这里用到了BlockingQueue的方法,很巧妙的使用。timed是判断这个线程是否可以回收,由我们设置的allowCoreThreadTimeOut和当前线程池线程大小决定的。当要回收,用的是poll方法,不能回收用的是take方法阻塞线程
线程池的shutdown()和shutdownNow();
- shutdown()方法会更改线程池的状态为SHUTDOWN状态,并不会立即终止。此时不能往线程池中添加任务,否则会抛出RejectedExecutionException异常。它会等线程池里的所有任务都处理完毕再退出线程。
- shutdownNow()方法会立即更改状态为STOP状态。并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,它会返回那些未执行的任务。其试图终止线程的方法是通过调用Thread.interrupt()方法来实现的。
提个问题,线程池有两种提交方式execute()和submit()区别,可以在评论区讨论哦!请关注微信公众号,查看,嘻嘻。不过以后会在评论区回复的!!
如果大家感觉满意,就点个喜欢吧!!哈哈
上一篇文章 java 线程和线程安全初识
如有什么不对的地方,欢迎大家指出来,我们共同学习!!
接下来会写关于 内部类 的文章,谢谢大家关注
欢迎关注我的微信公众号cobs-snail,让我们一起前进吧!!

网友评论
- 接受的参数不一样,submit 可以接实现Callable的线程。
- submit()有返回值,而execute()没有;
- submit()可以进行Exception处理; 通过Future.get() 抛出异常