线程池是池化技术的一种典型实现,所谓池化技术就是提前保存大量的资源,以备不时之需。在机器资源有限的情况下,使用池化技术可以大大的提高资源的利用率,提升性能等。
一、什么是线程池
线程池,说的就是提前创建好一批线程,然后保存在线程池中,当有任务需要执行的时候,从线程池中选一个线程来执行任务。
二、为什么使用线程池
为了减少创建和销毁线程的次数,让每个线程都可以多次的使用,可以根据系统情况调整线程的数量,防止消耗过多内存。
(1)降低资源消耗。通过重复利用机制已降低线程创建和销毁造成的消耗。
(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3)提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
三、线程池的实现方式
JDK1.5中引入的Executor框架把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行。
线程池的核心参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
(1)corePoolSize : 核心线程数,线程池维护线程的最少数量。一旦创建将不会再释放。如果创建的线程数还没有达到指定的核心线程数量,将会继续创建新的核心线程,直到达到最大核心线程数后,核心线程数将不在增加;如果没有空闲的核心线程,同时又未达到最大线程数,则将继续创建非核心线程;如果核心线程数等于最大线程数,则当核心线程都处于激活状态时,任务将被挂起,等待空闲线程来执行。
(2)maximumPoolSize : 最大线程数,允许创建的最大线程数量。如果最大线程数等于核心线程数,则无法创建非核心线程;如果非核心线程处于空闲时,超过设置的空闲时间,则将被回收,释放占用的资源。
(3)keepAliveTime : 也就是当线程空闲时,所允许保存的最大时间,超过这个时间,线程将被释放销毁,但只针对于非核心线程。
(4)unit : keepAliveTime的单位,TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
(5)workQueue : 线程池所使用的任务缓冲队列,存储暂时无法执行的任务,等待空闲线程来执行任务。
(6)threadFactory : 线程工程,用于创建线程,一般用默认的即可。
(7)handler : 线程池拒绝任务时的处理策略,当线程边界和队列容量已经达到最大时,用于处理阻塞时的程序。
Java通过Executors为我们提供了四种线程池的创建方式,分别为:
(1)newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
(2)newFixedThreadPool,创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
//创建可固定长度的线程池,只会创建10个线程池进行处理
ExecutorService fixedExecutorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 200; i++) {
final int temp = i;
// 可执行线程 execute 启动线程
fixedExecutorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + "," + temp);
}
});
}
//停止线程池
fixedExecutorService.shutdown();
newFixedThreadPool源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
(3)newCachedThreadPool,创建一个可缓存的线程池,创建的都是非核心线程,而且最大线程数为Interge的最大值,调用execute 将重用以前构造的线程(如果线程可用)。如果没有可用的线程,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。如果有大量耗时的任务,则不适该创建方式,它只适用于生命周期短的任务。
// 可缓存线程池 Executors表示启动线程的 可创建线程数是最大为Interge的最大值
ExecutorService cacheExecutorService = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
final int temp = i;
// 可执行线程 execute 启动线程
cacheExecutorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + "," + temp);
}
});
}
//停止线程池
cacheExecutorService.shutdown();
newCachedThreadPool源码:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
(4)newScheduledThreadPool,创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
//创建可定时执行的线程池 数量为10
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
for (int i = 0; i < 100; i++) {
final int temp=i;
//schedule 方法表示线程执行 表示延迟5秒之后 开始执行线程
scheduledExecutorService.schedule(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+""+temp);
}
}, 5, TimeUnit.SECONDS);
}
//停止线程池
scheduledExecutorService.shutdown();
newScheduledThreadPool源码:
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
四、自定义线程池
虽然JDK的Executors提供了4大类线程池供我们直接使用,但是在实际生产开发中,往往是不允许/不建议使用这4种直接调用的方式的。原因就是因为直接创建会容易导致OOM(内存溢出),所以推荐我们手工自定义线程池。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadExcuetorPoolUtil {
/**
* 核心线程数 = cpu 核心数 + 1
*/
private static int core = Runtime.getRuntime().availableProcessors() + 1;
public static ThreadPoolExecutor applepnpool =
new ThreadPoolExecutor(core, core * 2, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1000),
new ThreadPoolExecutor.AbortPolicy());
/**
* 返回线程池对象
*
* @return
*/
public static ThreadPoolExecutor getThreadPoolExecutor() {
return applepnpool;
}
}
以上就是关于java中使用线程池的详细用法,其中也包含了JDK源码的讲解,学会了这个教程,对于一遍的线程池的使用,不会有任何问题。