多线程实例--定时器的相关知识

文章介绍了定时器的概念,如其相当于闹钟,在预设时间后执行指定任务。在Java中,通过标准库的Timer类可以创建定时器,但该例子展示了如何手动创建一个更复杂的定时器,利用优先级阻塞队列管理多个任务,确保时间最短的任务优先执行。此外,文章还讨论了线程的前台和后台模式,以及如何避免忙等和死锁问题,提供了完整的自定义定时器实现代码。
摘要由CSDN通过智能技术生成

目录
1.定时器的定义
2.标准库里面定时器代码写法
3.自己实现一个定时器

定时器

  • 1.定义

  • 相当于闹钟,设定多长时间后执行某个动作.

  • 服务器开发过程中,客户端一般会请求服务器.客户端发出请求后,就需要等待服务器响应,但是也不是死等,一般会设置一个时间,超出时间限制不会进行死等.

  • 2.标准库里面定时器代码写法
    在这里插入图片描述

  • 一个定时器可以执行多个任务

在这里插入代码片
import java.util.Timer;
import java.util.TimerTask;

public class Demo23 {
    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("时间到快起床");
            }
        },1000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("时间到快刷牙");
            }
        },2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("时间到快洗脸");
            }
        },3000);
    }
}
  • 执行上述代码,进程并没有退出,Timer内部需要一组线程来执行任务,而这里的线程是前台线程
    前台线程:(main或者new Thread)默认创建的线程都是前台线程,前台线程阻止进程退出
    后台线程:不会阻止进程的退出,如果前台线程执行完,程序会立马退出(用setDaemon()设置成后台线程,必须在start调用之前开始设置)

  • 3.手动创建一个定时器(步骤)

  • 先描述里面的任务(执行什么工作,执行的时间)

在这里插入代码片
class Mytask {
    //要执行的任务
    private Runnable runnable;
    //什么时间来执行
    private long time;

    public Mytask(Runnable runnable, long delay) {
        this.runnable = runnable;
        //现在的时间
        this.time = System.currentTimeMillis()+delay;
    }
    //因为一会要观察现在时间和设定闹钟时间大小关系,所以要得到这个时间

    public Runnable getRunnable() {
        return runnable;
    }

    public long getTime() {
        return time;
    }
}
  • 让MyTimer实现管理多个任务(使用优先级队列和阻塞队列,既可以保证线程安全,也可以管理多个任务,使时间最短的任务先进行)
在这里插入代码片
class MyTimer{
    private BlockingQueue<Mytask> queue=new PriorityBlockingQueue<>();
}
  • 任务已经放入优先级阻塞队列里面,需要创建扫描线程,让线程不停检查队首元素,检查是否是否到
    阻塞队列无法阻塞式取队头元素,这时就需要取出任务,判断时间是否到,时间没有到又塞回队列
在这里插入代码片
lass MyTimer{
    private BlockingQueue<Mytask> queue=new PriorityBlockingQueue<>();
    //创建一个扫描线程
    //写了一个构造方法
    public MyTimer(){
        Thread t1=new Thread(()->{
            while (true){
                //取出队头元素
                try {
                    Mytask mytask = queue.take();
                    //判断当前时间和任务设定时间大小关系
                    long curTime = System.currentTimeMillis();
                    if (curTime >= mytask.getTime()) {
                        //线程运行
                        mytask.getRunnable().run();
                    } else {
                        //把队头元素塞回去
                        queue.put(mytask);
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
    }
  • 因为你的Mytask是自定义类型,需要自己实现比较
在这里插入代码片
class Mytask implements Comparable<Mytask>{
    //要执行的任务
    private Runnable runnable;
    //什么时间来执行
    private long time;

    @Override
    public int compareTo(Mytask o) {
        return (int)(this.time-o.time);
    }

如果是自定义类型需要比较大小,需要自己实现比较规则
1.实现Comparable接口(只能比较一个方面)

在这里插入代码片
class Student implements Comparable<Student>{
    String name;
    int age;

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;
    }
}

2.实现Comparator接口(可以多方面进行比较)

在这里插入代码片
class Student{
    String name;
    int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
class NameComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        return o1.age-o2.age;
    }
}
class AgeComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age-o2.age;
    }
}
public class Demo26 {
    public static void main(String[] args) {
     Student student1=new Student("小加",11);
     Student student2=new Student("小王",14);
     NameComparator nameComparator=new NameComparator();
        nameComparator.compare(student1,student2);
        AgeComparator ageComparator=new AgeComparator();
        ageComparator.compare(student1,student2);
    }
}
  • 下面这个代码一直在进行忙等,如果不想忙等,就需要使用wait,等其他线程任务进行唤醒在这里插入图片描述

可以进行加锁操作(如果扫描线程先执行take之后,线程切换到shedule线程,新增加一个任务,假设这个任务1点执行,shedue线程完毕之后,执行notify唤醒扫描线程,继续向下执行,假设当前时间是12点,任务是2点半,这时需要把任务塞回队列,然后进行wait等待2个半小时,这时我们shedule新增的任务不就错过了.所以我们要确保在take和wait之间notify不能执行,所以给他俩进行加锁

在这里插入代码片
 public MyTimer(){
        Thread t1=new Thread(()->{
            while (true) {
                //取出队头元素
                try {
                    synchronized (locker) {
                        Mytask mytask = queue.take();
                        //判断当前时间和任务设定时间大小关系
                        long curTime = System.currentTimeMillis();
                        if (curTime >= mytask.getTime()) {
                            //线程运行
                            mytask.getRunnable().run();
                        } else {
                            //把队头元素塞回去
                            queue.put(mytask);
                            //没到点就进行等待
                            locker.wait(mytask.getTime()-curTime);
                        }
                      }
                    } catch(InterruptedException e){
                        throw new RuntimeException(e);
                    }
                }
            });
            t1.start();
        }
    //取任务
    public void schedule(Runnable runnable,long after) throws InterruptedException {
        Mytask mytask=new Mytask(runnable,after);
        synchronized (locker){
            queue.put(mytask);
            locker.notify();
        }
    }
}

如果这样则会出现死锁情况

在这里插入代码片
public void schedule(Runnable runnable,long after) throws InterruptedException {
        synchronized (locker){
            Mytask mytask=new Mytask(runnable,after);
            queue.put(mytask);
            locker.notify();
        }
    }

假设先进行task取队首元素,此时队首就为空就触发阻塞进行等待,此时shedule要先进行加锁,才能给队首放元素,但是shedule获取不到锁,所以代码处于死锁状态

4.自己实现定时器的完整代码

在这里插入代码片
import java.util.PriorityQueue;
import java.util.Timer;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;

//这个类表示一个任务
class Mytask implements Comparable<Mytask>{
    //要执行的任务
    private Runnable runnable;
    //什么时间来执行
    private long time;

    @Override
    public int compareTo(Mytask o) {
        return (int)(this.time-o.time);
    }

    public Mytask(Runnable runnable, long delay) {
        this.runnable = runnable;
        //现在的时间
        this.time = System.currentTimeMillis()+delay;
    }
    //因为一会要观察现在时间和设定闹钟时间大小关系,所以要得到这个时间

    public Runnable getRunnable() {
        return runnable;
    }

    public long getTime() {
        return time;
    }
}
class MyTimer{
    private BlockingQueue<Mytask> queue=new PriorityBlockingQueue<>();
    private Object locker=new Object();//定义一个锁对象
    //创建一个扫描线程
    //写了一个构造方法
    public MyTimer(){
        Thread t1=new Thread(()->{
            while (true) {
                //取出队头元素
                try {
                    synchronized (locker) {
                        Mytask mytask = queue.take();
                        //判断当前时间和任务设定时间大小关系
                        long curTime = System.currentTimeMillis();
                        if (curTime >= mytask.getTime()) {
                            //线程运行
                            mytask.getRunnable().run();
                        } else {
                            //把队头元素塞回去
                            queue.put(mytask);
                            //没到点就进行等待
                            locker.wait(mytask.getTime()-curTime);
                        }
                      }
                    } catch(InterruptedException e){
                        throw new RuntimeException(e);
                    }
                }
            });
            t1.start();
        }
    //放任务
    public void schedule(Runnable runnable,long after) throws InterruptedException {
        Mytask mytask=new Mytask(runnable,after);
        queue.put(mytask);
        synchronized (locker){
            locker.notify();
        }
    }
}
public class Demo24 {
    public static void main(String[] args) throws InterruptedException {
        MyTimer myTimer=new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("时间1");
            }
        },1000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("时间2");
            }
        },2000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("时间3");
            }
        },3000);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值