线程池简单点说就是:多个线程封装在一起进行操作,这就形成了线程池。
有这么一个场景,一个包工头承担了一个活,然后找来了20个人,对他们说:兄弟们!这个活你们20个人三天之内把它完成啊!在这里,这20个人就相当于20个线程,因为这个活任务量比较大,如果一个人做的话可能得花费几年的时间才能完成,而现在我找了20个人和我一起做这个活,那么我可能一个月就完成了这个活。
基于以上形象的比喻,我们在这里可以知道,为了完成一个复杂的任务,我们需要把多个线程捆绑一起执行,并放在一个池子里面,这个池子就是线程池。
但是在现实生活中,可能还会出现以下场景:
1,这个活很大,有多少人我要多少人来完成。
2,这个活很大,但是我只要20个人来完成。
3,这个活很大,但是我只要一个人来完成。
这三种情况分别对应:无限多,限定长度,单线程。
从JDK1.5之后追加了一个并发访问的程序包:java.util.concurrent,对于此线程池的操作的核心类和接口就定义在此包之中。着这个包里面有两个很重要的接口:
以下几个类都是在java.util.concurrent这个包下面:
interface ScheduledExecutorService//这个是调度线程池,意思就是工作可以自动定时完成
interface ExecutorService//普通的执行线程池
如果要进行线程池的创建,一般可以使用Executors这个类完成。
1,创建一个大小不受限制的线程池(意思就是说这个活干不完的话随时可以增加线程):
public static ExecutorService newCachedThreadPool()
2,创建固定大小的线程池
public static ExecutorService newFixedThreadPool(int nThreads)
3,这个任务虽然很大,但是我只想一个人完成(单线程池)
public static ScheduledExecutorService newSingleThreadScheduleExecutor()
接下来讲解怎么实现或者是创建线程池:
1,创建无限多个线程的线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestDemo{
public static void main(String args[]) throws Exception{
ExecutorService executorService=Executors.newCachedThreadPool();
for(int x=0;x<10;x++) {
int index=x;
executorService.submit(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()
+",x="+index);
}
});
}
executorService.shutdown();
}
}
其他的线程池启动可以自己查看文档自己实验。
线程池给开发者带来的好处是允许多个线程按照组的模式处理,这样在某一个业务业务逻辑非常复杂的环境下性能就会得到很好的提升。
使用线程池的好处:诸如Web服务器,数据库服务器,文件服务器或者邮件服务器之类的许多服务器,都面向处理来自某些远程来源的大量短小的任务,请求以某种方式到达服务器,这种方式可能通过某种协议或者通过JMS队列,或者通过轮询数据库。不管如何到达,服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。每当一个请求到达就创建一个新的线程,然后在新的线程中为请求服务,但是频繁的创建线程,销毁线程所带来的系统开销其实是非常大的。
线程池线程生命周期开销问题和资源不足不问题提供了解决方案,通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上,其好处是:因为在请求到达时线程已经存在,所以在无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使得应用程序响应更快。而且,通过适当的线程池中线程的数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。
Demo1:通过new创建线程时,除非调用prestartAllCoreThreads方法初始化核心线程,否则此时线程池中有0个线程,即使工作队列中存在多个任务,同样不会执行。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String args[]) throws InterruptedException {
LinkedBlockingQueue<Runnable> objects=new LinkedBlockingQueue<>();
for (int i=0;i<10;i++) {
objects.put(()->{
System.out.println(Thread.currentThread().getName());
});
}
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(10,20,60L,TimeUnit.SECONDS,objects);
threadPoolExecutor.prestartAllCoreThreads();
}
}
任务数x,核心线程数csize,工作队列的容量nWorks
1,如果x<=csize,只启动x个线程,
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String args[]) throws InterruptedException {
LinkedBlockingQueue<Runnable> objects=new LinkedBlockingQueue<>();
/* for (int i=0;i<10;i++) {
objects.put(()->{
System.out.println(Thread.currentThread().getName());
});
}
*/
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(10,20,60L,TimeUnit.SECONDS,objects);
threadPoolExecutor.prestartAllCoreThreads();
for(int i=0;i<5;i++) {
threadPoolExecutor.submit(()->{
System.out.println(threadPoolExecutor.getActiveCount());
});
}
}
}
2,x>=csize&&x<nWorks+csize,这种情况最多只会启动10个线程(会启动<=csize个线程,其他的任务就放到任务队列中)
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String args[]) throws InterruptedException {
LinkedBlockingQueue<Runnable> objects=new LinkedBlockingQueue<>(20);
/* for (int i=0;i<10;i++) {
objects.put(()->{
System.out.println(Thread.currentThread().getName());
});
}
*/
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(10,20,60L,TimeUnit.SECONDS,objects);
threadPoolExecutor.prestartAllCoreThreads();
for(int i=0;i<20;i++) {
threadPoolExecutor.submit(()->{
System.out.println(threadPoolExecutor.getActiveCount());
});
}
}
}
3,当x>csize&&x>nWorks+csize.
这个情况又分为两种情况:当x-nWorks<=mSize(最大线程数),就启动x-nSize个线程
当x-nWorks>mSize:hui启动mSize个线程,其余的执行相应的拒绝策略
下面讲解使用Executors实例化线程池:
1,ExecutorService executorService1=Executors.newCachedThreadPool();
2,ExecutorService executorService2=Executors.newFixedThreadPool(2);
3,ScheduledExecutorService scheduledExecutorService=Executors.newScheduledThreadPool(1);
4,ExecutorService executorService3=Executors.newSingleThreadExecutor();
5,ExecutorService executorService4=Executors.newWorkStealingPool();
6,ScheduledExecutorService scheduledExecutorService1=Executors.newSingleThreadScheduledExecutor();
1,创建一个可以根据需要创建新线程的线程池,如果有空闲线程,优先使用空闲线程
2,创建一个固定大小的线程池,在任何时候最多只有N个线程在处理任务
3,能延迟执行的,定时执行的线程池
4,单一线程,核心数和最大只有1个,只会使用单一的线程来执任务
5,工作窃取,一部分很忙,一部分很闲,很闲的线程就会去窃取很忙的那部分线程进行处理,使用多个队列来减少竞争
6,单一的可以定时执行定时调度的线程池
线程池实用注意事项:
1,尽量不要使用Executors来创建线程池
2,核心线程数尽量要小,因为大话容易引起上下文的切换
再次学习线程池,再次记录:
线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出的数量的线程排队等候,等待其它线程执行完毕,再从队列中取出任务来齿形。
它的主要特点为:线程复用,控制最大开发数,管理线程。