#冲刺创作新星#java多线程——定时器、线程池 原创
定时器
定时器是什么
定时器也是软件开发中的一个
重要组件
. 类似于一个 “闹钟
”. 达到一个设定的时间之后, 就执行某个指定好的代码.
定时器是一种
实际开发
中非常常用的组件.
比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连.
比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自动删除).
类似于这样的场景就需要用到定时器
.
标准库中的定时器
- 标准库中提供了一个
Timer
类. Timer 类的核心方法为schedule
. - schedule 包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后- 执行 (单位为
毫秒
).
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello");
}
}, 3000);
定时器的构成:
- 一个带优先级的阻塞队列
- 队列中的每个元素是一个 Task 对象.
- Task 中带有一个时间属性, 队首元素就是即将
- 同时有一个 worker 线程一直扫描队首元素, 看队首元素是否需要执行
import java.util.concurrent.PriorityBlockingQueue;
class MyTask implements Comparable<MyTask>{
//任务要具体干啥
private Runnable runnable;
// 任务具体啥时候干. 保存任务要执行的毫秒级时间戳
private long time ;
//after 是一个时间间隔 不是绝对的时间戳的值
public MyTask(Runnable runnable, long delay){
this.runnable = runnable;
this.time = System.currentTimeMillis()+delay;
}
public void run(){
runnable.run();
}
public long getTime() {
return time;
}
@Override
public int compareTo(MyTask o) {
return (int)(this.time - o.time);
}
}
class MyTimer {
//定时器内部要能够存放多个任务
private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
public void shedule(Runnable runnable, long delay) {
MyTask task = new MyTask(runnable, delay);
queue.put(task);
// 每次任务插入成功之后, 都唤醒一下扫描线程, 让线程重新检查一下队首的任务看是否时间到要执行~~
synchronized (locker) {
locker.notify();
}
}
private Object locker = new Object();
public MyTimer() {
Thread t = new Thread(() -> {
while (true) {
try {
//先取出首元素
MyTask task = queue.take();
// 再比较一下看看当前这个任务时间到了没?
long curTime = System.currentTimeMillis();
if (curTime < task.getTime()) {
//时间没到 把任务再塞回队列中
queue.put(task);
//给定一个指定时间
synchronized (locker) {
locker.wait(task.getTime() - curTime);
}
} else {
//时间到了 执行这个任务
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
public class demo4 {
public static void main(String[] args) {
MyTimer myTimer = new MyTimer();
myTimer.shedule(new Runnable() {
@Override
public void run() {
System.out.println("hello timer!");
}
},5000);
System.out.println("main");
}
}
最先在控制台输出 main 然后过5秒输出hello timer!
线程池
线程池是一种
多线程
处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程
。每个线程都使用默认的堆栈大小
,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值
。超过最大值的线程可以排队,但他们要等到其他线程完成后
才启动。
线程池的优点:
1、降低资源消耗:减少线程的创建和销毁带来的性能开销。
2、提高响应速度:当任务来时可以直接使用,不用等待线程创建
3、可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。
标准库中的线程池
- 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
- 返回值类型为 ExecutorService
- 通过 ExecutorService.submit 可以注册一个任务到线程池中.
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
实现线程池
核心操作为 submit
, 将任务加入线程池中
使用 Worker
类描述一个工作线程. 使用 Runnable
描述一个任务.
使用一个 BlockingQueue
组织所有的任务
每个 worker
线程要做的事情: 不停的从 BlockingQueue
中取任务并执行.
指定一下线程池中的最大线程数 maxWorkerCount
; 当当前线程数超过这个最大值时, 就不再新增
线程了.
实现线程池
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
class MyThreadPoll{
//线程池
private BlockingQueue<Runnable> queue = new LinkedBlockingDeque<>();
public void sumit(Runnable runnable)throws InterruptedException{
queue.put(runnable);
}
public MyThreadPoll(int m){
for(int i = 0;i < m;i++){
Thread t =new Thread(()->{
while (true){
try {
Runnable runnable = queue.take();
runnable.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
}
public class demo5 {
public static void main(String[] args) throws InterruptedException {
MyThreadPoll poll = new MyThreadPoll(10);
for(int i = 0;i < 1000;i++){
int taskId = i;
poll.sumit(new Runnable() {
@Override
public void run() {
System.out.println("执行当前任务:"+taskId+"当前线程"+Thread.currentThread().getName());
}
});
}
}
}