标签:pool qq群 cin 通过 csdn 信息 cache throw 检查
只有光头才能变强
回顾前面:
本篇主要是讲解线程池,这是我在多线程的倒数第二篇了,后面还会有一篇死锁。主要将多线程的基础过一遍,以后有机会再继续深入!
那么接下来就开始吧,如果文章有错误的地方请大家多多包涵,不吝在评论区指正哦~
声明:本文使用JDK1.8
线程池可以看做是线程的集合。在没有任务时线程处于空闲状态,当请求到来:线程池给这个请求分配一个空闲的线程,任务完成后回到线程池中等待下次任务(而不是销毁)。这样就实现了线程的重用。
我们来看看如果没有使用线程池的情况是这样的:
public class ThreadPerTaskWebServer {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(80);
while (true) {
// 为每个请求都创建一个新的线程
final Socket connection = socket.accept();
Runnable task = () -> handleRequest(connection);
new Thread(task).start();
}
}
private static void handleRequest(Socket connection) {
// request-handling logic here
}
}
为每个请求都开一个新的线程虽然理论上是可以的,但是会有缺点:
所以说:我们的线程最好是交由线程池来管理,这样可以减少对线程生命周期的管理,一定程度上提高性能。
JDK给我们提供了Excutor框架来使用线程池,它是线程池的基础。
下面我们来看看JDK线程池的总体api架构:
接下来我们把这些API都过一遍看看:
Executor接口:
ExcutorService接口:
AbstractExecutorService类:
ScheduledExecutorService接口:
ThreadPoolExecutor类:
ScheduledThreadPoolExecutor类:
除了ScheduledThreadPoolExecutor和ThreadPoolExecutor类线程池以外,还有一个是JDK1.7新增的线程池:ForkJoinPool线程池
于是我们的类图就可以变得完整一些:
JDK1.7中新增的一个线程池,与ThreadPoolExecutor一样,同样继承了AbstractExecutorService。ForkJoinPool是Fork/Join框架的两大核心类之一。与其它类型的ExecutorService相比,其主要的不同在于采用了工作窃取算法(work-stealing):所有池中线程会尝试找到并执行已被提交到池中的或由其他线程创建的任务。这样很少有线程会处于空闲状态,非常高效。这使得能够有效地处理以下情景:大多数由任务产生大量子任务的情况;从外部客户端大量提交小任务到池中的情况。
来源:
学到了线程池,我们可以很容易地发现:很多的API都有Callable和Future这么两个东西。
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)
其实它们也不是什么高深的东西~~~
我们可以简单认为:Callable就是Runnable的扩展。
也就是说:当我们的任务需要返回值的时,我们就可以使用Callable!
Future一般我们认为是Callable的返回值,但他其实代表的是任务的生命周期(当然了,它是能获取得到Callable的返回值的)
简单来看一下他们的用法:
public class CallableDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以执行Runnable对象或者Callable对象代表的线程
Future<Integer> f1 = pool.submit(new MyCallable(100));
Future<Integer> f2 = pool.submit(new MyCallable(200));
// V get()
Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1);
System.out.println(i2);
// 结束
pool.shutdown();
}
}
Callable任务:
public class MyCallable implements Callable<Integer> {
private int number;
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int x = 1; x <= number; x++) {
sum += x;
}
return sum;
}
}
执行完任务之后可以获取得到任务返回的数据:
这是用得最多的线程池,所以本文会重点讲解它。
我们来看看顶部注释:
变量ctl定义为AtomicInteger,记录了“线程池中的任务数量”和“线程池的状态”两个信息。
线程的状态:
各个状态之间转换:
下面我就列举三个比较常见的实现池:
如果读懂了上面对应的策略呀,线程数量这些,应该就不会太难看懂了。
一个固定线程数的线程池,它将返回一个corePoolSize和maximumPoolSize相等的线程池。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
非常有弹性的线程池,对于新的任务,如果此时线程池里没有空闲线程,线程池会毫不犹豫的创建一条新的线程去处理这个任务。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
使用单个worker线程的Executor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
我们读完上面的默认实现池还有对应的属性,再回到构造方法看看
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;
}
再总结一遍这些参数的要点:
线程数量要点:
线程空闲时间要点:
排队策略要点:
当线程关闭或者线程数量满了和队列饱和了,就有拒绝任务的情况了:
拒绝任务策略:
execute执行方法分了三步,以注释的方式写在代码上了~
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//如果线程池中运行的线程数量<corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果线程池中运行的线程数量>=corePoolSize,且线程池处于RUNNING状态,且把提交的任务成功放入阻塞队列中,就再次检查线程池的状态,
// 1.如果线程池不是RUNNING状态,且成功从阻塞队列中删除任务,则该任务由当前 RejectedExecutionHandler 处理。
// 2.否则如果线程池中运行的线程数量为0,则通过addWorker(null, false)尝试新建一个线程,新建线程对应的任务为null。
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);
}
// 如果以上两种case不成立,即没能将任务成功放入阻塞队列中,且addWoker新建线程失败,则该任务由当前 RejectedExecutionHandler 处理。
else if (!addWorker(command, false))
reject(command);
}
ThreadPoolExecutor提供了shutdown()
和shutdownNow()
两个方法来关闭线程池
shutdown() :
shutdownNow():
区别:
本篇博文主要简单地将多线程的结构体系过了一篇,讲了最常用的ThreadPoolExecutor线程池是怎么使用的~~~
明天希望可以把死锁写出来,敬请期待~~~
还有剩下的几个线程池(给出了参考资料):
参考资料:
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友
文章的目录导航:
标签:pool qq群 cin 通过 csdn 信息 cache throw 检查
原文地址:https://www.cnblogs.com/Java3y/p/8996365.html