目录
🐽案例三、线程池
🐇线程池是什么
🐬标准库中的线程池
🦌实现线程池
🎨案例四、定时器
🎧定时器是什么
🎼标准库中的定时器
🥁实现定时器
🐽案例三、线程池
🐇线程池是什么
最初引入线程,就是因为进程太重了,频繁创建销毁进程,开销比较大,"大/小"是相对的,随着业务上对于性能要求越来越高,对应的线程创建/销毁的频次,越来越多,此时线程创建/销毁的开销变的比较明显,无法忽略不计了。
线程池就是解决上述问题的常见方案,线程池就是把线程提前从系统中申请好,放到一个地方后面需要使用线程的时候,直接从这个地方来取,而不是从系统重新申请,线程用完了之后,也是还回到刚才这个地方。线程池最⼤的好处就是减少每次启动、销毁线程的损耗。
为啥我们说,从线程池里取线程,比从系统申请,来的更高效呢?
内核态与用户态都是操作系统的概念
操作系统=操系统内核(操作系统的核心功能部分负责完成一个操作系统的核心工作(管理))+操作系统配套的应用程序对应的执行的很多代码逻辑都是要用户态的代码和内核态的代码配合完成的
应用程序有很多,这些应用程序,都是由内核统一负责管理和服务
内核里的工作就可能是非常繁忙的=>提交给内核要做的任务可能是不可控的
从系统创建线程,就相当于让银行的人给我复印,这样的逻辑就是调用系统API,由系统内核执行一系列逻辑来完成这个过程
直接从线程池里取,这就相当于是自助复印,整个过程都是纯用户态代码,都是咱们自己控制的,整个过程更可控,效率更高
因此,通常认为,纯用户态操作,就比经过内核的操作,效率更高
🐬标准库中的线程池
JavaAPI文档🔗🔗🔗
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class Demo32 {public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(4);for (int i = 0; i < 100; i++) {int id=i;service.submit(() -> {Thread current = Thread.currentThread();System.out.println("hello Thread" + id + "," + current.getName());});}} }
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class Demo32 {public static void main(String[] args) throws InterruptedException {ExecutorService service = Executors.newFixedThreadPool(4);for (int i = 0; i < 100; i++) {int id = i;service.submit(() -> {Thread current = Thread.currentThread();System.out.println("hello Thread" + id + "," + current.getName());});}// 最好不要立即终止,可能使任务还没执行完呢,线程就终止了Thread.sleep(2000);// 把线程池里所有的线程都终止掉service.shutdown();System.out.println("程序退出");} }
🦌实现线程池
👁🗨当前线程池,核心代码就写到这里,更多的功能,支持更多的参数以及扩容/拒绝策略,写起来比较麻烦的
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue;class MyThreadPool {private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);//此处 n 表示创建几个线程public MyThreadPool(int n) {// 先创建出 n 个线程for (int i = 0; i < n; i++) {Thread t = new Thread(() -> {// 循环的从队列中取任务while (true) {try {Runnable runnable = queue.take();runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}}//添加任务public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);} }public class Demo33 {public static void main(String[] args) throws InterruptedException {MyThreadPool pool = new MyThreadPool(4);for (int i = 0; i < 1000; i++) {int id = i;pool.submit(() -> {System.out.println("执行任务" + id + "," + Thread.currentThread().getName());});}} }
🎨案例四、定时器
🎧定时器是什么
⏰定时器也是软件开发中的⼀个重要组件,类似于⼀个 "闹钟", 达到⼀个设定的时间之后, 就执⾏某个指定好的代码。
🕑定时器是一种实际开发中非常常用的组件,
🕒比如网络通信中,如果对方500ms内没有返回数据,则断开连接尝试重连
🕓比如一个Map,希望里面的某个key在3s之后过期(自动删除).
🕔类似以于这样的场景就需要用到定时器
🎼标准库中的定时器
👁🗨先来看一段代码
import java.util.Timer; import java.util.TimerTask;public class Demo34 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello");}}, 3000);System.out.println("程序开始运行");} }
打印到控制台的时候,hello延迟打印了
▪️标准库中提供了一个Timer类,Timer类的核心方法为schedule
▫️schedule包含两个参数,第一个参数指定即将要执行的任务代码,第二个参数指定多长时▪️间之后执行(单位为毫秒)👁🗨可以再测一个
import java.util.Timer; import java.util.TimerTask;public class Demo34 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello3");}}, 3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello2");}}, 2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello1");}}, 1000);System.out.println("程序开始运行");} }
🥁实现定时器
📝📝📝
1.创建类,描述一个要执行的任务是啥(任务的内容,任务的时间)
2.管理多个任务,通过一定的数据结构,把多个任务存起来
3.有专门的线程,执行这里的任务
1️⃣Timer类提供的核心接口为schedule,用于注册一个任务,并指定这个任务多长时间后执行,Task类用于描述一个任务(作为Timer的内部类),里面包含一个Runnable对象和一个time(毫秒时
间戳),这个对象需要放到优先队列中,因此需要实现Comparable接口。
class MyTimerTask {private Runnable runnable;//此处这里的 time,通过毫秒时间戳,表示这个任务具体啥时候执行private long time;private MyTimerTask(Runnable runnable, long delay) {this.runnable = runnable;this.time = System.currentTimeMillis() + delay;}public void run() {runnable.run();}public long getTime() {return time;}
}
注意🔴🔵⚫️⚪️
2️⃣Timer实例中,通过PriorityQueue来组织若干个Task对象,通过schedule来往队列中插入一个个Task对象。
import java.util.PriorityQueue;class MyTimerTask implements Comparable<MyTimerTask> {private Runnable runnable;//此处这里的 time,通过毫秒时间戳,表示这个任务具体啥时候执行private long time;private MyTimerTask(Runnable runnable, long delay) {this.runnable = runnable;this.time = System.currentTimeMillis() + delay;}public void run() {runnable.run();}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {// 此处这里的 - 的顺序,就决定了这里是大堆还是小堆// 此处需要小堆return (int) (this.time - o.time);}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
}public class Demo35 {public static void main(String[] args) {}
}
3️⃣Timer类中存在一个worker线程,一直不停的扫描队首元素,看看是否能执行这个任务,所谓"能执行"指的是该任务设定的时间已经到达了。
import java.util.PriorityQueue;class MyTimerTask implements Comparable<MyTimerTask> {private Runnable runnable;//此处这里的 time,通过毫秒时间戳,表示这个任务具体啥时候执行private long time;public MyTimerTask(Runnable runnable, long delay) {this.runnable = runnable;this.time = System.currentTimeMillis() + delay;}public void run() {runnable.run();}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {// 此处这里的 - 的顺序,就决定了这里是大堆还是小堆// 此处需要小堆return (int) (this.time - o.time);}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public MyTimer() {// 创建线程,负责执行上述队列中的内容Thread t = new Thread(() -> {while (true) {if (queue.isEmpty()) {continue;}MyTimerTask current = queue.peek();// 比如,当前时间是 10:30,任务时间是 12:00,不应该执行// 如果当前时间是 10:30,任务时间是 10:29,应该执行if (System.currentTimeMillis() >= current.getTime()) {// 要执行任务current.run();//把执行过的任务,从队列中删除queue.poll();} else {// 先不执行任务continue;}}});t.start();}public void schedule(Runnable runnable,long delay){MyTimerTask myTimerTask=new MyTimerTask(runnable, delay);queue.offer(myTimerTask);}
}public class Demo35 {public static void main(String[] args) {}
}
🛑🛑🛑还存在线程安全问题
PriorityQueue这个类自身,不带线程安全的控制能力,并且又是多个线程来进行操作,一定存在线程安全问题的风险~~
import java.util.PriorityQueue;class MyTimerTask implements Comparable<MyTimerTask> {private Runnable runnable;//此处这里的 time,通过毫秒时间戳,表示这个任务具体啥时候执行private long time;public MyTimerTask(Runnable runnable, long delay) {this.runnable = runnable;this.time = System.currentTimeMillis() + delay;}public void run() {runnable.run();}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {// 此处这里的 - 的顺序,就决定了这里是大堆还是小堆// 此处需要小堆return (int) (this.time - o.time);}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();private Object locker = new Object();public MyTimer() {// 创建线程,负责执行上述队列中的内容Thread t = new Thread(() -> {while (true) {synchronized (locker) {if (queue.isEmpty()) {continue;}MyTimerTask current = queue.peek();// 比如,当前时间是 10:30,任务时间是 12:00,不应该执行// 如果当前时间是 10:30,任务时间是 10:29,应该执行if (System.currentTimeMillis() >= current.getTime()) {// 要执行任务current.run();//把执行过的任务,从队列中删除queue.poll();} else {// 先不执行任务continue;}}}});t.start();}public void schedule(Runnable runnable, long delay) {synchronized (locker) {MyTimerTask myTimerTask = new MyTimerTask(runnable, delay);queue.offer(myTimerTask);}}
}public class Demo35 {public static void main(String[] args) {MyTimer myTimer = new MyTimer();myTimer.schedule(() -> {System.out.println("hello 3000");}, 3000);myTimer.schedule(() -> {System.out.println("hello 2000");}, 2000);myTimer.schedule(() -> {System.out.println("hello 1000");}, 1000);System.out.println("程序开始执行");}
}
🧐🧐🧐还存在的问题
🌼🌼🌼完整代码
import java.util.PriorityQueue;class MyTimerTask implements Comparable<MyTimerTask> {private Runnable runnable;//此处这里的 time,通过毫秒时间戳,表示这个任务具体啥时候执行private long time;public MyTimerTask(Runnable runnable, long delay) {this.runnable = runnable;this.time = System.currentTimeMillis() + delay;}public void run() {runnable.run();}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {// 此处这里的 - 的顺序,就决定了这里是大堆还是小堆// 此处需要小堆return (int) (this.time - o.time);}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();private Object locker = new Object();public MyTimer() {// 创建线程,负责执行上述队列中的内容Thread t = new Thread(() -> {try {while (true) {synchronized (locker) {while (queue.isEmpty()) {locker.wait();}MyTimerTask current = queue.peek();// 比如,当前时间是 10:30,任务时间是 12:00,不应该执行// 如果当前时间是 10:30,任务时间是 10:29,应该执行if (System.currentTimeMillis() >= current.getTime()) {// 要执行任务current.run();//把执行过的任务,从队列中删除queue.poll();} else {// 先不执行任务locker.wait(current.getTime() - System.currentTimeMillis());}}}} catch (InterruptedException e) {e.printStackTrace();}});t.start();}public void schedule(Runnable runnable, long delay) {synchronized (locker) {MyTimerTask myTimerTask = new MyTimerTask(runnable, delay);queue.offer(myTimerTask);locker.notify();}}
}public class Demo35 {public static void main(String[] args) {MyTimer myTimer = new MyTimer();myTimer.schedule(() -> {System.out.println("hello 3000");}, 3000);myTimer.schedule(() -> {System.out.println("hello 2000");}, 2000);myTimer.schedule(() -> {System.out.println("hello 1000");}, 1000);System.out.println("程序开始执行");}
}
🎯定时器这个东西,特别重要,特别常用,尤其是后端开发中,和"阻塞队列”类似也会有专门服务器,就是用来在分布式系统中实现定时器这样的效果