什么是线程池?
线程池顾名思义就是线程的容器,是用来管理线程的。
当我们在创建并实现线程时,如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
而线程池的好处,就是可以方便的管理线程,也可以减少内存的消耗。
使用线程池的优点
- 降低资源消耗:通过重复利用已创建的线程降低线程和销毁带来的消耗。
- 提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行。
- 提高线程的可管理性:使用线程池可以统一进行线程分配、调度和监控。
线程池继承关系
线程池的创建
可以通过ThreadPoolExecutor来创建一个线程:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
其参数:
- corePoolSize:线程池的基本大小。
- maximumPoolSize:(线程池最大数量)线程池允许创建的最大线程数。
- keepAliveTime:(线程活动保持时间)线程池的工作线程空闲后,保持存活的时间。
- TimeUnit :线 程 活 动 保 持 时 间 的 单 位 。
- BlockingQueue:任务队列,用于保存等待执行的任务的阻塞队列。
- RejectedExecutionHandler :饱和策略。
其阻塞队列又分为五种:
- ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。
- LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。
- SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操 作 一 直 处 于 阻 塞 状 态 , 吞 吐 量 通 常 要 高 于 Linked-BlockingQueue 。
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
手工创建一个线程池:
ThreadPoolExecutor execotor = new ThreadPoolExecutor(
2, 5, 1, TimeUnit.MINUTES,new LinkedBlockingDeque<>());
向线程池提交任务
execute()方法
execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。
submit()方法
submit()方法用于提交需要返回值的任务。
如何使用?
在解释如何关闭线程池之后一起写一个实例展示如何使用…(如下)
关闭线程池
一旦shutdown()或者shutdownNow()执行完毕,线程池就进入TERMINATED状态,此时线程池就结束了。
- shutdown()
将线程池的状态设置为SHUTDOWN状态。然后中断所有没有正在执行任务的线程。 - shutdownNow()
将线程的状态设置为stop,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。 - isShutdown()
描述的是非RUNNING状态,也就是SHUTDOWN/ STOP/ TERMINATED三种状态。只要调用了shutdown()和shutdownNow()方法中的任何一个都会返回true。 - isTerminating()
描述的是SHUTDOWN和STOP两种状态。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法才会返回true。
//关闭线程池
execotor.shutdown(); //方法一:安全的关闭
execotor.shutdownNow(); //方法二:立即关闭
System.out.println(execotor.isShutdown()); // true
System.out.println(execotor.isTerminating()); // true / false
向线程池提交任务和关闭线程池的实例:
package pool;
import java.util.concurrent.*;
public class TestThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor execotor = new ThreadPoolExecutor(
2, 5,1,TimeUnit.MINUTES,new LinkedBlockingDeque<>());
// 设置线程运行三次
for(int i=0; i<3; i++) {
// Runnable
// submit()方法实现
execotor.submit(()-> System.out.println(Thread.currentThread().getName() + ":submit"));
// execute()方法实现
execotor.execute(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":execute");
});
// Callable
// submit()方法实现,带返回值
Future<String> future = execotor.submit(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
});
try {
System.out.println(future.get());
System.out.println();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
//关闭线程池
execotor.shutdown(); //安全的关闭
execotor.shutdownNow(); //立即关闭
System.out.println(execotor.isShutdown()); //true
System.out.println(execotor.isTerminating());
}
}
结果展示:
Executor框架
其框架结构:
ThreadPoolExecutor
Executor框架最核心的类是ThreadPoolExecutor,它是线程池的实现类。
- 创建无大小限制的线程池
public static ExecutorService
- 创建固定大小的线程池
public static ExecutorService new FixedThreadPool(int nThreads)
- 单线程池
public static ExecutorService newSingleThreadExecutor()
FixedThreadPool
可重用固定线程数的线程池。
FixedThreadPool 使 用 无 界 队 列 LinkedBlockingQueue 作 为 线 程 池 的 工 作 队 列 ( 队 列 的 容 量 为Integer.MAX_VALUE)。
适用范围:
FixedThreadPool适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场合,适用于负载比较重的服务器。
实例:
package pool;
import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TestExecutor {
public static void main(String[] args) {
// 1.用户自定义线程工厂
// 2.开发中建议使用自定义的线程工厂(阿里开发Java规约)
ThreadFactory threadFactory = new ThreadFactory() {
private final AtomicInteger id = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("Thread-Pool_Task-" + id.getAndIncrement());
return thread;
}
};
ExecutorService service = Executors.newFixedThreadPool(5,threadFactory);
for (int i=0; i<10; i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+ " " + LocalDateTime.now().toString());
}
});
}
if(!service.isTerminated()) {
service.shutdown();
}
}
}
SingleThreadPoolExecutor
使用单个worker线程的Executor。
SingleThreadExecutor 的 corePoolSize 和 maximumPoolSize 被 设 置 为 1 。 其 他 参 数 与 FixedThreadPool 相 同 。SingleThreadExecutor 使 用 无 界 队 列 LinkedBlockingQueue 作 为 线 程 池 的 工 作 队 列 ( 队 列 的 容 量 为Integer.MAX_VALUE)。
适用范围:
SingleThreadExecutor适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景。
实例:
package pool;
import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TestExecutor {
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
for (int i=0; i<10; i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
if (!service.isTerminated()) {
service.shutdown();
}
}
}
CachedThreadPool
是一个会根据需要创建新线程的线程池
CachedThreadPool的corePoolSize被设置为0,即corePool为空maximumPoolSize被设置为Integer。MAX_VALUE,即maximumPool是无界的。这里把keepAliveTime设置为60L,意味着CachedThreadPool中的空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被终止。
适用范围:
CachedThreadPool是大小无界的线程池,适用于执行很多的短期异步任务的小程序,或者负载较轻的服务器。
实例:
package pool;
import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TestExecutor {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.submit(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
System.out.println(Thread.currentThread().getName()+"、"+j);
}
}
});
}
executorService.shutdown();
}
}
ScheduledThreadPoolExecutor
是一个可以周期性执行任务的类(使用不多)
实例:
package pool;
import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TestExecutor {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
// 周期性任务
// 1.指定执行时间
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "当前时间" + LocalDateTime.now());
}
}, 2, TimeUnit.MINUTES);
// 指定周期性执行
scheduledExecutorService.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 周期性 当前时间" + LocalDateTime.now());
}
}, 0, 2, TimeUnit.SECONDS
);
}
}