15. 多线程案例(3)——定时器

Java
311
0
0
2022-11-28
标签   Java多线程

定时器可以强制终止请求:浏览器内部都有一个定时器,发送了请求之后,定时器就开始计时。如果在打开浏览界面的时候,浏览器的响应时间过了响应时间,就会强制终止请求。

1.定时器的构成

1.使用一个类来描述“一个逻辑”,也就是要执行的任务,同时也要记录这个任务啥时候来执行 2.使用一个 阻塞优先队列(既支持阻塞的特性,又支持按优先级的“先进先出”,实际上是堆) 来组织若干个Task,也就是收让队首元素为最早的任务,如果队首元素的时间还没到,那么其他元素肯定也不能执行 3.需要有一个扫描线程,要循环检测队首元素是否需要执行这个任务

2.定时器代码实现

package day0302;

import java.util.concurrent.PriorityBlockingQueue;

public class ThreadDemo22 {
    static class Task implements Comparable<Task>{
        //Runnable中有run方法,可以借助这个方法,来描述要执行的具体任务 
        private Runnable command;
        //time表示啥时候来执行command 
        private long time;//time是一个绝对时间

        //after 的意思事表示多少毫秒之后执行(相对时间) 
        public Task(Runnable command, long after){
            this.command = command;
            this.time = System.currentTimeMillis()+after;
        }

        //具体执行任务的逻辑 
        public void run(){
            command.run();
        }


        @Override 
        public int compareTo(Task o) {
            //排序:谁的时间少谁先执行 
            return (int)(this.time - o.time);
        }
    }

    static class Worker extends Thread{
        private PriorityBlockingQueue<Task> queue = null;

        public Worker(PriorityBlockingQueue<Task> queue) {
            this.queue = queue;
        }

        @Override 
        public void run() {
            //实现具体的线程执行的内容 
            while (true){
                try {
                    //1.取出队首元素,检查时间是否到了
                    Task task= queue.take();
                    //2.检查时间是否到了 
                    long curTime = System.currentTimeMillis();
                    if (task.time > curTime){
                        //时间还没到,就把任务塞回队列中
                        queue.put(task);
                    }else {
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }
    static class Timer{
        //定时器的构成 
        //1.使用一个类来描述“一个逻辑”,也就是要执行的任务,同时也要记录这个任务啥时候来执行 
        //2.使用一个 阻塞优先队列(既支持阻塞的特性,又支持按优先级的“先进先出”,实际上是堆) 来组织若干个Task 
        private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();//标准库中的阻塞优先队列 
        //3.需要有一个扫描线程,要循环检测队首元素是否需要执行这个任务 
        public Timer(){
            //创建线程 
            Worker worker = new Worker(queue);
            worker.start();
        }
        //4.还需要提供一个方法,让调用者能把任务给“安排”进来 
        public void schedule(Runnable command, long after){
            Task task = new Task(command,after);
            queue.put(task);
        }
    }

    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new Runnable() {
            @Override 
            public void run() {
                System.out.println("hh");
            }
        },5000);
    }

}

但是以上代码执行过程中,可能会出现忙等,也就是以下代码导致的

img

这是可以借助wait/notify来解决

那么最终代码为:

import java.util.concurrent.PriorityBlockingQueue;

public class ThreadDemo22 {
    static class Task implements Comparable<Task>{
        //Runnable中有run方法,可以借助这个方法,来描述要执行的具体任务 
        private Runnable command;
        //time表示啥时候来执行command 
        private long time;//time是一个绝对时间

        //after 的意思事表示多少毫秒之后执行(相对时间) 
        public Task(Runnable command, long after){
            this.command = command;
            this.time = System.currentTimeMillis()+after;
        }

        //具体执行任务的逻辑 
        public void run(){
            command.run();
        }


        @Override 
        public int compareTo(Task o) {
            //排序:谁的时间少谁先执行 
            return (int)(this.time - o.time);
        }
    }

    static class Worker extends Thread{
        private PriorityBlockingQueue<Task> queue = null;
        private Object mailBox = null;

        public Worker(PriorityBlockingQueue<Task> queue,Object mailBox) {
            this.queue = queue;
            this.mailBox = mailBox;
        }

        @Override 
        public void run() {
            //实现具体的线程执行的内容 
            while (true){
                try {
                    //1.取出队首元素,检查时间是否到了
                    Task task= queue.take();
                    //2.检查时间是否到了 
                    long curTime = System.currentTimeMillis();
                    if (task.time > curTime){
                        //时间还没到,就把任务塞回队列中
                        queue.put(task);
                        synchronized (mailBox){
                            mailBox.wait(task.time - curTime);
                        }
                    }else {
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }
    static class Timer{
        //为了避免盲等,需要使用wait方法 
        private Object mailBox = new Object();
        
        //定时器的构成 
        //1.使用一个类来描述“一个逻辑”,也就是要执行的任务,同时也要记录这个任务啥时候来执行 
        //2.使用一个 阻塞优先队列(既支持阻塞的特性,又支持按优先级的“先进先出”,实际上是堆) 来组织若干个Task 
        private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();//标准库中的阻塞优先队列 
        //3.需要有一个扫描线程,要循环检测队首元素是否需要执行这个任务 
        public Timer(){
            //创建线程 
            Worker worker = new Worker(queue,mailBox);
            worker.start();
        }
        //4.还需要提供一个方法,让调用者能把任务给“安排”进来 
        public void schedule(Runnable command, long after){
            Task task = new Task(command,after);
            queue.put(task);
            synchronized (mailBox){
                mailBox.notify();
            }
        }
    }

    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new Runnable() {
            @Override 
            public void run() {
                System.out.println("hh");
                timer.schedule(this,2000);
            }
        },2000);
    }

}