目录
1.定时器
2.自己实现一个定时器
2.1执行任务
2.2组织任务
2.3执行时间到了的任务
执行结果:
完整代码:
3.线程池
4.自己实现一个线程池
定时器类似于一个闹钟,进行定时,在一定时间后,被唤醒并执行某个之前设定好的任务
java标准库中提供了一个定时器(java.util.Timer),核心方法只有一个schedulle,有两个参数:任务是啥,多长时间后执行
import java.util.Timer;
import java.util.TimerTask;
public class Demo9 {
public static void main(String[] args) {
Timer timer = new Timer();
//schedele里面有两个参数,一个是描述任务,一个时设置时间
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("my task");
}
},3000);
System.out.println("main");
}
}
想要实现一个定时器,我们就必须要了解Timer内部都需要什么东西?
①描述一个任务
②组织任务
③执行时间到了的任务
创建一个专门的类来表示一个定时器中的任务(TimerTask)
使用一定的数据结构把一些任务放到一起,这里我们可以思考,安排任务的时候我们可以是无序的,例如先安排半小时之后干什么,然后在安排十分钟之后干什么,安排任务的时间是随机的,但是执行任务的顺序并不是随机的,肯定是时间小的先执行,不仅如此,此处的数据结构还要考虑线程安全问题,可能在多个线程里进行注册任务
在这里我们使用 PriorityBlockingQueue 作为组织任务的数据结构
需要有一个线程不停的去检查当前优先队列的队首元素,看看当前最靠前的任务是不是时间到了
我们可以发现报错,其实上述代码有两个比较严重的问题
1)MyTask并没有去设置比较规则
2)忙等问题,在检查队首元素的时候,由于是while(true),就会导致这个循环执行的非常快,如果队列不为空,并且时间也没有到,此时CPU既没有实质性的工作产出也没有进行休息,非常浪费CPU
import java.util.concurrent.PriorityBlockingQueue;
//实现一个计时器
class MyTask implements Comparable{
private Runnable runnable;
private long time;
public MyTask(Runnable runnable,long delay){
this.runnable = runnable;
this.time = System.currentTimeMillis()+delay;
}
public long getTime(){
return time;
}
public void run() {
runnable.run();
}
@Override
public int compareTo(MyTask o) {
return (int)(this.time - o.time);
}
}
class MyTimer{
private PriorityBlockingQueue queue = new PriorityBlockingQueue<>();
Object locker = new Object();
public void schedule(Runnable runnable , long delay){
MyTask task = new MyTask(runnable,delay);
queue.put(task);
synchronized (locker){
locker.notify();
}
}
public MyTimer(){
Thread thread = new Thread(() -> {
while(true){
try {
MyTask task = queue.take();
long currTime = System.currentTimeMillis();
if(currTime < task.getTime()){
queue.put(task);
//解决忙等问题
synchronized (locker){
locker.wait(task.getTime() - currTime);
}
}else{
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
}
public class Demo10 {
public static void main(String[] args) {
MyTimer timer = new MyTimer();
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
},3000);
System.out.println("main");
}
}
将线程提前创建好放进池子里,后面需要用到线程直接从池子里拿就可以了,线程用完之后也不需要还给系统,直接放回池子里就好了
为什么线程放在池子里会比从系统申请线程快呢?
这里就牵扯到内核态和用户态,对于我们日常写的代码就是用户态运行的,而有些代码需要调用操作系统的API,进一步的逻辑就会在内核中执行,所以在内核运行的代码我们就称为是内核态
为什么用户态会更快呢?举个例子,去餐厅吃饭,突然杯子没有水了,这时候有两个选择,一是自己去倒水(相当于用户态),二是呼叫服务员(相当于内核态),如果是自己去倒水目标就很明确,而呼叫服务员时,目标可能就不那么明确,有可能在中途被其他人叫走了,这样就造成了很多不确定因素
通过这个例子我们也就知道为什么用户态会比内核态速度更快。创建线程本身就需要内核的支持,调用start(),其实归根结底也是要进入内核态来运行,而把创建好的线程放到池子里,由于池子就是通过用户掏来实现的,这个过程就不涉及到内核
java标准库的线程池
ThreadPoolExecutor
参数非常的多,但是最主要的就是前两个参数,可以将线程池比作是一个工厂,里面的线程比作是员工
1.int corePoolSize:核心线程数(相当于工厂里的正式员工)
2.int maximumPoolSize:做大线程数(相当于正式员工加上临时员工)
3.long keepAliveTime:(允许临时工摸鱼的时间)
4. TimeUnit unit,:时间单位
5.BlockingQueue
workQueue:任务队列,线程池会提供一个submit方法,让我们把任务注册到线程池当中,并且加入到这个队列当中 6.ThreadFactory threadFactory:线程工厂,线程是怎么被创建出来的
7. RejectedExecutionHandler handler:拒绝策略,当队列任务满了怎么办,怎么解决
java标准库中简化版的线程池
Executors,本质是对ThreadPoolExecutor的一种封装,提供一些参数
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//java系统的简化版线程池
public class Demo11 {
public static void main(String[] args) {
//创建一个固定数目的线程池
ExecutorService poor = Executors.newFixedThreadPool(10);
//创建一个自动扩容的线程池
Executors.newCachedThreadPool();
//创建一个只有一个线程的线程池
Executors.newSingleThreadExecutor();
//创建一个带有定时器功能的线程池
Executors.newScheduledThreadPool();
//可以在线程池里创建很多个任务,150个任务被十个线程分配
for (int i = 0; i < 150; i++) {
//通过runnable来指定任务
poor.submit(new Runnable() {
@Override
public void run() {
System.out.println("hallo thread");
}
});
}
}
}
实现线程池的几个步骤;
1.需要描述任务(可以使用Runnable)
2.需要能够组织任务(使用数据结构)
3.能够描述工作线程
4.需要组织这些线程
5.需要实现往线程池里添加任务
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
//自己实现一个线程池
class MyThreadPool{
//描述和组织任务
private BlockingQueue queue = new LinkedBlockingQueue<>();
//描述一个线程,工作线程就是的功能就是任务队列中取出任务并执行
static class Worker extends Thread{
private BlockingQueue queue = null;
//
public Worker(BlockingQueue queue){
this.queue = queue;
}
@Override
public void run() {
while(true){
//循环的去读取线程中的任务
try {
Runnable runnable = queue.take();
runnable.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private List workers = new ArrayList<>();
//创建一个构造方法,创建出若干个线程,放到数组当中
public MyThreadPool(int n){
for (int i = 0; i < n; i++) {
Worker worker = new Worker(queue);
worker.start();
workers.add(worker);
}
}
//创建一个方法允许我们放任务到线程池当中
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
}
public class Demo12 {
static int count = 0;
public static void main(String[] args) throws InterruptedException {
MyThreadPool pool = new MyThreadPool(10);
for (int i = 0; i < 100; i++) {
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello word" + count);
count++;
}
});
}
}
}