线程的每次启动和销毁都需要消耗很大的性能, 所以引入线程池; 线程池最大的好处就是减少每次启动, 销毁线程的损耗.
(1) 下面以一个送快递的案例具体引入 JDK 的线程池:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadVsThreadPool {
public static void main(String[] args) {
// 1. 没有使用线程, 送快递
// 相当于老板自己送快递, 再干自己的活
System.out.println("送快递到北京"); // 模拟送快递, 有可能送快递是比较耗时的
System.out.println("送快递到上海");
System.out.println("处理自己的业务");
// 2. 使用手动创建线程的方式, 送快递
// 相当于雇佣了两个人, 他们在送快递, 此时老板也在干自己的事情
new Thread(()->{
System.out.println("送快递到北京");
}).start();
new Thread(()->{
System.out.println("送快递到上海");
}).start();
System.out.println("处理自己的业务");
// 3. 使用 JDK 的线程池来送快递
// 创建线程池对象, 相当于开了一家送快递的公司, 专门处理送快递的任务
ThreadPoolExecutor pool = new ThreadPoolExecutor(
4, // 核心线程数: 快递公司的正式员工 ---- 线程
10, // 最大线程数: 快递公司的总员工(正式工 + 临时工) ---- 线程
// 临时工 + 空闲时间: 正式员工数量不够处理任务的时候, 招聘临时工, 临时工常超过空闲时间, 就解雇
60, // 空闲时间数
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(1000), // 阻塞队列: 快递公司的仓库, 用来保存快递包裹 ---- 存放线程的容器
new ThreadFactory() { // 匿名内部类
@Override
public Thread newThread(Runnable r) { // 线程的工厂类: 快递公司招聘标准 ---- 创建线程的方式
return new Thread(r);
}
},
// 拒绝策略: 接受到新的快递单, 但此时仓库容量不够存放快递包裹
// new ThreadPoolExecutor.AbortPolicy() // 抛异常的方式:RejectedExecutionException
// new ThreadPoolExecutor.CallerRunsPolicy() // 谁把包裹交给我的, 让他自己去送(execute 代码所在线程自己执行)
// new ThreadPoolExecutor.DiscardOldestPolicy() // 把仓库中最旧的包裹丢弃
new ThreadPoolExecutor.DiscardPolicy() // 把仓库中最新的丢掉
);
pool.execute(()->{
System.out.println("送快递到北京");
});
pool.execute(()->{
System.out.println("送快递到上海");
});
System.out.println("处理自己的业务");
}
}
这里分别使用三种方法实现送快递操作, 分别是没有使用线程, 手动创建线程,
使用 JDK 的线程池来送快递
对线程池的属性加以介绍:
(2) 下面介绍一些简单的线程池
他们都是默认一些属性的值, 方便使用
ExecutorService pool = Executors.newSingleThreadExecutor();
ExecutorService pool2 = Executors.newFixedThreadPool(4);
ExecutorService pool3 = Executors.newScheduledThreadPool(4);
ExecutorService pool4 = Executors.newCachedThreadPool();
(3) 使用一下固定大小的线程池(FixedThreadPool)
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class runFixedThreadPoolTest {
private static ExecutorService FIXED_POOL = Executors.newFixedThreadPool(4);
// 固定大小的线程池
public static void runFixedThreadPool(Runnable task) {
FIXED_POOL.execute(task);
}
public static void main(String[] args) {
runFixedThreadPool(()->{
System.out.println("送快递到北京");
});
runFixedThreadPool(()->{
System.out.println("送快递到上海");
});
System.out.println("干自己的事情");
}
}
(4) 使用一下计划任务线程池(定时任务线程 ScheduledThreadPool)
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolTest {
private static ScheduledExecutorService SCHEDULED_POOL = Executors.newScheduledThreadPool(4);
public static void main(String[] args) {
SCHEDULED_POOL.scheduleAtFixedRate(()->{
Date date = new Date();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(df.format(date));
}, 1, // 延迟多少秒之后执行一次
1, // 每间隔多少秒执行一次
TimeUnit.SECONDS); // 时间单位
SCHEDULED_POOL.scheduleAtFixedRate(()->{
System.out.println("abc");
}, 0, 2, TimeUnit.SECONDS);
}
}
分析一下这个打印结果:
(5) 自己实现一个定时器(定时任务线程池)
import java.util.Date;
import java.util.concurrent.PriorityBlockingQueue;
public class MyScheduledThreadPool {
//员工
private MyTimer[] threads; // 这里可以去掉, 直接在构造方法里 new 就可以了
// 仓库(优先级队列)
private PriorityBlockingQueue<MyTimerTask> queue = new PriorityBlockingQueue<>();
public MyScheduledThreadPool(int capacity) {
threads = new MyTimer[capacity];
for (int i = 0; i < capacity; i++) {
threads[i] = new MyTimer(queue);
threads[i].start();
}
}
// 模拟执行定时任务的方法: 任务, 延迟时间(毫秒), 间隔时间
public void schedule(Runnable runnable, long delay, long period) {
MyTimerTask task = new MyTimerTask(runnable, new Date().getTime() + delay, period);
// 这里考虑到一种特殊情况: 如果有个线程的延迟时间是很久, 另外一个延迟时间短的要先执行, 就会出错, 所以必须使用 notify进行唤醒
synchronized (queue) {
queue.put(task);
queue.notifyAll();
}
}
// 员工
private static class MyTimer extends Thread {
private PriorityBlockingQueue<MyTimerTask> queue;
public MyTimer(PriorityBlockingQueue<MyTimerTask> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
while (true) {
// take 和 put 方法在达到上限和下限的时候, 会阻塞等待
// poll 和 offer 是非阻塞方法. 如果达到上限和下限的时候, 返回空
MyTimerTask task = queue.take();
synchronized (queue) {
long current = System.currentTimeMillis(); // 获取当前时间戳
if (current < task.next) {
// 如果当前时间小于执行下一个的时间, 就等待
queue.wait(task.next - current);
// 等待直到满足下次执行时间, 需要从仓库中重新获取包裹 ---> 可能有时间更短的任务(更紧急的包裹需要派送)
queue.put(task); // 之前已经从仓库取出来在等待了, 要重新放回去, 此处并没有执行
} else {
task.run();
if (task.period > 0) {
// 如果时间间隔大于0
task.next = task.next + task.period; // 下次执行时间修改为当前的 next + period
queue.put(task);
}
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 定时任务
private static class MyTimerTask implements Runnable, Comparable<MyTimerTask> {
private Runnable runnable;
private long next; // 下次执行时间
private long period; // 间隔时间
public MyTimerTask(Runnable runnable, long next, long period) {
this.runnable = runnable;
this.next = next;
this.period = period;
}
@Override
public void run() {
runnable.run();
}
@Override
public int compareTo(MyTimerTask o) {
return Long.compare(next, o.next);
}
}
public static void main(String[] args) {
MyScheduledThreadPool pool = new MyScheduledThreadPool(4);
pool.schedule(()->{
System.out.println("ABC");
}, 999999999,1000);
pool.schedule(()->{
System.out.println("D");
}, 0,1000);
}
}
(6) 自己实现一个 JDK 的线程池
package Java05_28;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class MyThreadPool {
private BlockingQueue<Runnable> queue; // 仓库: 可以使用自定义的阻塞式队列
// 创建快递公司
public MyThreadPool(int corePoolSize, int capacity) {
// 创建仓库
queue = new ArrayBlockingQueue<>(capacity);
// 招聘员工
for (int i = 0; i < corePoolSize; i++) {
new MyThread(queue).start();
}
}
// 快递公司开放送快递的接口(营业点), 客户调用这个接口(去营业点办业务), 快递仓库存放包裹(往阻塞队列里面加元素)
private void execute(Runnable task) throws InterruptedException {
queue.put(task);
}
// 正式员工
private static class MyThread extends Thread{
private BlockingQueue<Runnable> queue;
public MyThread(BlockingQueue<Runnable> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
// 员工不停的从仓库取包裹, 没有进入到仓库的就阻塞等待, 取到了就执行
while (true) {
Runnable task = queue.take();
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}