(三)线程池原理分析-JAVA线程池原理分析
什么是线程池
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序
都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,
还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用
线程池,必须对其实现原理了如指掌。
线程池作用
线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。
如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。
线程池四种创建方式
(1)newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
总结: 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
总结:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()
CPU密集型时,任务可以少配置线程数,大概和机器的cpu核数相当,这样可以使得每个线程都在执行任务
IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数
Linux 查看CPU逻辑个数
cat /proc/cpuinfo |grep "processor"|wc -l
Linux 如何查看每个CPU核心数。
cat /proc/cpuinfo |grep "cores"
(3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
总结:创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下
(4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序
总结:(FIFO, LIFO, 优先级)执行。
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
corePoolSize:核心线程数
maximumPoolSize:最大创建线程数
keepAliveTime:存活时间(线程空闲超时时间,创建了一直不用,节约内存)
TimeUnit :时间单位
workQueue:等待队列
threadFactory:线程工程
handler:拒绝策略
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;
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
CachedThreadPool采用同的SynchronousQueue;
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
FixedThreadPool采用的是LinkedBlockingQueue;
class TaskThread implements Runnable{
private String threadName;
public TaskThread(String threadName) {
this.threadName = threadName;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+threadName);
}
}
ThreadPoolExecutor threadPoolExecutor
= new ThreadPoolExecutor(1, 2, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1));
TaskThread taskThread1 = new TaskThread("任务1");
TaskThread taskThread2 = new TaskThread("任务2");
TaskThread taskThread3 = new TaskThread("任务3");
TaskThread taskThread4 = new TaskThread("任务4");
threadPoolExecutor.execute(taskThread1);
threadPoolExecutor.execute(taskThread2);
threadPoolExecutor.execute(taskThread3);
threadPoolExecutor.execute(taskThread4);
???以上这段代码运行会报错吗 ???
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task [email protected] rejected from [email protected][Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
maximumPoolSize是2,LinkeBlockingQueue大小是1,所以最大可同时运行3个线程。如加入任务>maximumPoolSize+queueSize,则任务拒绝,所以抛出RejectedExecutionException异常
pool-1-thread-1任务1
pool-1-thread-1任务2
前两个线程使用都是Thread-1
==================
第三个线程使用的Thread-2,为新创建的线程
pool-1-thread-2任务3