线程池知识点总结
ThreadPoolExecutor是Java提供的线程池的类
Executor
创建出固定线程的线程池/自动扩容线程数的线程池
int corePoolSize和int maximumPoolSize
核心线程数和最大线程数
long keepAliveTime和TimeUnit unit
非核心线程允许空闲的最大时间
ThreadFactory threadFactory
接口,实现newThread
RejectedExecutionHandler handler
拒绝策略

线程池的模拟实现
java
class MyThreadPool{
private static BlockingQueue<Runnable> qeque=null;
public MyThreadPool(int n){
qeque=new ArrayBlockingQueue<>(n);
for (int i = 0; i < n; i++) {
Thread thread = new Thread(() -> {
try {
while (true) {
Runnable task = qeque.take();
task.run();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
thread.start();
}
}
public void submit(Runnable runnable) throws InterruptedException {
qeque.put(runnable);
}
}
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool=new MyThreadPool(10);
for (int i = 0; i < 100; i++) {
int id=i;
myThreadPool.submit(()->{
System.out.println("hello:"+id+","+Thread.currentThread().getName());
});
}
}
}
线程池最核心的就是submit这样的操作,往线程池里添加任务(任务就是runnable),同时得有线
程来执行任务
在构造方法中把线程创建出来,因为随时都有新的任务被添加进来,所以线程就需要持续不断的尝
试执行读取任务,取到了就执行,没取到就阻塞等待
java
for (int i = 0; i < n; i++) {
Thread thread = new Thread(() -> {
try {
while (true) {
Runnable task = qeque.take();
task.run();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
thread.start();
当任务执行完毕之后,我们发现虽然任务已经结束了,但是进程仍然没有结束

对于这个现象是因为线程池里的这些线程,还在take阻塞的(等待),同时线程池里的线程是后台
线程,组织进程结束(可以通过setDaemon()方法设置为前台线程)
进程的退出码
正常退出

异常退出

定时器
定时器类似于闹钟,时间到了,执行一些逻辑
Timer
Java标注库的定时器,正常描述任务是Runnable,在定时器这里稍微特殊一点,把Runnable封装
了一下TimerTask

观察TimerTake这个方法,发现要求实现Runnable接口,核心还是重写run方法
定时器的使用
java
public static void main(String[] args) {
Timer timer1= new Timer();
timer1.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello timer,3000");
}
},3000);
Timer timer2= new Timer();
timer2.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello timer,2000");
}
},2000);
Timer timer3= new Timer();
timer3.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello timer,1000");
}
},1000);
}

对于new TimerTask(){}是匿名内部类的写法
- 创建了子类
- 重写了run
- new了子类的实例
模拟实现定时器
- 创建一个类表示一个任务
- 定时器中能够管理多个任务,必须使用一些集合类把这些任务管理起来。当管理多个任务的时候, 需要确保时间最早的任务最先执行. 通过遍历的方式, 找到时间最早.
- 实现schedule方法,把任务添加到队列即可
- 额外创建一个线程,负责执行队列中的任务。定时器和线程池不同,线程池是只要队列不为空, 就立即取任务并执行.此处需要看队首元素的时间是否到了,时间到才能执行, 时间不到不能执行.
对于我们自己设定的定时器,必须要明确比较规则,可以通过实现compareTo接口

这里需要把自己定义的类传进去

任务在当前时刻为基准,多长时间之后开始执行

获取当前时刻时间的api,返回的是一个long,ms级别的时间戳

循环等待,等到queue中出现新的数据
使用while就是为了"再次确认,条件是否满足",当前wait唤醒之后通过总的while循环刚好就是再走
一遍确认逻辑

这样的操作,创建了一个带有线程池的定时器
具体代码如下
java
class MyTimerTask implements Comparable<MyTimerTask> {
private long time;
private Runnable task;
public MyTimerTask(Runnable task, long time) {
this.time = time;
this.task = task;
}
@Override
public int compareTo(MyTimerTask o) {
return (int) (this.time - o.time);
}
public long getTime() {
return time;
}
public void run() {
task.run();
}
}
class MyTimer {
public static PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
public void schedule(Runnable task, long delay) {
synchronized (this) {
MyTimerTask timerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);
queue.offer(timerTask);
this.notify();
}
}
public MyTimer() {
Thread t = new Thread(() -> {
while (true) {
while (queue.isEmpty()) {
try {
synchronized (this) {
this.wait();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
MyTimerTask task = queue.peek();
if (System.currentTimeMillis() < task.getTime()) {
try {
synchronized (this) {
this.wait(task.getTime() - System.currentTimeMillis());
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
task.run();
queue.poll();
}
}
});
t.start();
}
}
public class Demo04 {
public static void main(String[] args) {
MyTimer timer = new MyTimer();
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
}, 3000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
}, 2000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
}, 1000);
Executors.newScheduledThreadPool(4);
}
}
标准库提供的 Timer 和 自己写 MyTimer 差不多,都是使用一个线程负责扫描队首元素并执的, 如
果任务少/任务的时间分散都无所谓. 如果任务特别多, 时间非常集中~~ 一个线程就可能执行不过来
其他实现方式
定时器, 除了基于 堆(优先队列) 方式来实现的定时器之外,还有一种方案, 基于 "时间轮"类似搞个循
环队列 (数组),每个元素是一个 "时间单位"而且每个元素又是一个链表,每到一个时间单位, 光标
指向下一个元素, 同时把这个元素上对应链表中的任务都执行一遍
时间轮总结
优势是性能更高,劣势时间精度不如优先队列.
定时器运用
由于定时器, 是一个非常重要的组件, 在分布式系统中, 把定时器专门提取出来, 封装成一个单独的
服务器 (和消息队列是很像)