-
##线程池的好处
频繁的创建线程,销毁线程,会造成很大的性能开销,线程池技术可以人为的控制线程池的创建和销毁,可以提前创建好线程,放在线程池中,需要的时候就获取。 -
线程池主要有以下几个属性构成
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:线程空闲时间
TimeUnit :空闲时间单位
workQueue:等待队列 -
三种常用的线程池
newCachedThreadPool:无界线程池,该类型的线程池的的核心线程池的数量为0 ,也就是说没有核心线程池,最大线程池的数量是Integer.Max_value,其队列是SynchronouseQueue。其特点就是只要有新的任务进来就创建新的线程,直到资源用尽。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newFixedThreadPool:有界线程池,有固定大小的核心线程池数量和最大线程池数量,其等待队列是LinkedBlockingQueue。其特点是,核心线程数和最大线程数都是一样的,但是等待队列可以无限增大,知道资源用尽。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行
class ScheduledThreadDemo extends Thread{
@Override
public void run() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(df.format(System.currentTimeMillis()));
System.out.println("正在运行的线程名字" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
ExecutorService executorService4 = Executors.newScheduledThreadPool(1);
ScheduledThreadDemo scheduledThreadDemo = new ScheduledThreadDemo();
((ScheduledExecutorService) executorService4).scheduleAtFixedRate(scheduledThreadDemo,5,5, TimeUnit.SECONDS);
4.几种常见的队列
SynchronousQueue:此策略可以避免在处理可能具有内部依赖性的请求集时出现锁,一般搭配的是没有核心线程,最大线程为无数,所以每次任务都会创建新的线程,任务先加在队列中,只有被线程取走任务才能加入新的任务。
LinkedBlockingQueue:无界队列,当任务数大于等于核心线程的时候,就可以无限把任务加到无界队列中。
ArrayBlockingQueue:有界队列,使用复杂,但是合理使用可以有效防止资源耗尽。
5.如何设定线程池线程数:
5.1:CPU密集型:本地逻辑运行,占用cpu比价多,线程数小一些
5.2:I/O密集型:网络调用,DB调用,缓存调用之类,线程数可以大些。因为需要等待时间,但是不占CPU.
如果业务不是很复杂,CPU密集型可以设置为N+1,IO密集型设置2N的线程数(Netty源码也是默认用的这个公式)。如果业务涉及很多业务逻辑,可以使用:线程数=N(CPU核数)*(1+(线程等待时间)/(线程时间运行时间)),这个除法公式的核心思想是等待时间占比越大,线程数就越大,尽量让多个线程去抢占运算资源,否则线程都IO阻塞了,浪费资源。
Runtime.getRuntime().availableProcessors()可以获取可使用的CPU数。