阻塞队列的安全实现,定时器的安全实现(面试易考),超详细(二)

简介: 阻塞队列的安全实现,定时器的安全实现(面试易考),超详细

三、💜

定时器:日常开发中,常用到的组件(前后端都要用的定时器,类似于一个闹钟)

Timer类(util包)

public class Demo1 {
    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.schedule(new TimerTask() {             //给Timer中注册的任务,不是在调用
            @Override                             schedule的线程中执行的,而是通过Timer     
            public void run() {                 内部的线程来执行的   
                System.out.println("hello");
            }
        },3000);                            //过了这些时间才开始执行的
     }
}

注意:schedule可以设置多个任务,任务的完成先后是根据等待时间的

四、💚

如何我们自己实现一个定时器(闹钟):首先先要把任务描述出来,再用数据结构来把多个任务组织起来。

1.创建一个TimerTask这样的类,表示一个任务,该任务需包含两方面,任务的内容,任务的实际执行时间->通过时间戳表示:在schedule的时候,先获取当前系统时间,在这个基础上,加上(延时执行),得到了真实要执行这个任务的时间

2.使用一定的数据结构,把多个TimerTask组织起来

思路1(看看行不行):咱们用(链表/数组)组织TimerTask,如果任务特别多,如何确定哪个任务,何时能够执行呢?这时候就需要搞一个线程,不断的去遍历List,进行遍历,看着里面的每个元素,是否到了时间,时间到就执行,不到就跳过下一个。

回答:不行,这个思路可以,但是非常不好,假如任务执行时间都特别长,你这段时间一直都在遍历,扫描,会很消耗cpu(还没做事)。

思路2(正解):

1.并不需要扫描全部的任务,我们其实只需要关注一个,就是最早,时间最靠前的任务即可,最前面的任务假如还没到达的话,其他的任务更不会到达!!!

遍历一个->只关注一个,那么也就是说最好能选出最大,最小这种的数据结构(脑子里第一个肯定就是——堆(优先级队列))。

2.针对这个任务扫描,也不用一遍一遍一直重复执行,我们获取完队首时间(最小的任务时间)和当前时间做差值,在这个差值到达之前,可以让他休眠或者等待,这样不会进行重复的扫描,大幅度降低了扫描的次数->这样也不消耗cpu,(此处提高效率,不是缩短执行时间(定时器,时间是固定的),减少了资源利用率,避免了不必要的浪费)

战损没有改良的简版。

class MyTimertask{
    private long time;                 //任务什么时候执行,毫秒级的时间戳
    private Runnable runnable;         //具体的任务
    public MyTimertask(Runnable runnable,long delay){
        time=System.currentTimeMillis()+delay;
        this.runnable=runnable;
    }
    public long getTime() {
        return time;
    }
    public Runnable getRunnable() {
        return runnable;
    }
}
class MyTimer {
    //使用优先级队列
    private PriorityQueue<MyTimertask> queue = new PriorityQueue<>();
    //定时器核心方法
    public void schedule(Runnable runnable, long delay) {
        MyTimertask task = new MyTimertask(runnable, delay); 
        queue.offer(task);
    }
    //定时器还要搞个扫描方法,内部的线程。一方面负责监控队首元素是否到点了,是否应该执行,
    // 一方面当任务到了之后,就要调用这里的Runable的run方法执行任务
    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                //队列为空,此时不可以取元素
                if (queue.isEmpty()) {
                    continue;
                }
                MyTimertask Task = queue.peek();
                long curTime = System.currentTimeMillis();
                if (curTime >= Task.getTime()) {
                    queue.poll();
                    Task.getRunnable().run();
                } else {
                    try {
                        Thread.sleep(Task.getTime() - curTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
         t.start();
    }
}

当前战损版的问题:

1.线程不安全,PriorityQueue<MyTimeTask> queue=new PriorityQueue<>();

这个集合类不是线程安全的,他这个既在线程中使用,又在扫描线程中使用,给针对queue的操作进行加锁。(直接无脑暴力加锁)

2.扫描线程中的sleep是否合理呢?

(1)sleep阻塞后,不会释放锁(休眠的时候,最短的时间都还很长,这么大段时间无法添加任务)影响其他线程执行添加

(2)slepp在休眠过程中,不方便提前中断,(虽然可以用interrupt来中断,单用interrupt意味线程应该结束了)

假设当前最近的任务是14:00执行,当前时刻是13:00(sleep一小时),此时新增一个新的任务,新任务13:30要执行(成为了最早的任务),所以为了中断,应该使用wait

(3)我们设置的时候,需要把可比较写上,也就是说Comparable/Comparaor,也要知道,是什么需要比较呢,时间(顺序记不清无所谓,试一下就OK了)。

import java.util.PriorityQueue;
//创建一个类,用来描述定时器中的一个任务
class MyTimertask implements Comparable<MyTimertask>{
    private long time;                 //任务什么时候执行,毫秒级的时间戳
    private Runnable runnable;         //具体的任务
    public MyTimertask(Runnable runnable,long delay){
        time=System.currentTimeMillis()+delay;
        this.runnable=runnable;
    }
    public long getTime() {
        return time;
    }
    public Runnable getRunnable() {
        return runnable;
    }
    @Override
    public int compareTo(MyTimertask o) {
        return  (int)(this.time-o.time);
    }
}
class MyTimer {
    //使用优先级队列
    private PriorityQueue<MyTimertask> queue = new PriorityQueue<>();
    //定时器核心方法
    public void schedule(Runnable runnable, long delay) {
        synchronized (this) {
            MyTimertask task = new MyTimertask(runnable, delay);
            queue.offer(task);                     //新来一个任务,就需要把线程唤醒
            this.notify();
        }
    }
    //定时器还要搞个扫描方法,内部的线程。一方面负责监控队首元素是否到点了,是否应该执行,
    // 一方面当任务到了之后,就要调用这里的Runable的run方法执行任务
    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                synchronized (this) {
                    //队列为空,此时不可以取元素
                    while (queue.isEmpty()) {
                        try {
                            this.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    MyTimertask Task = queue.peek();               //任务的时间
                    long curTime = System.currentTimeMillis();     //当前的系统时间
                    if (curTime >= Task.getTime()) {               //系统时间
                        queue.poll();               14:01>任务时间14:00 ——执行任务
                        Task.getRunnable().run();
                    } else {
                        try {
                          this.wait(Task.getTime() - curTime);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
         t.start();
    }
}
public class Demo3 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }, 3000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("god");
            }
        }, 2000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("good");
            }
        }, 1000);
    }
}
相关文章
|
7月前
|
消息中间件 安全 Java
面试官:单例Bean一定不安全吗?实际工作中如何处理此问题?
面试官:单例Bean一定不安全吗?实际工作中如何处理此问题?
107 5
|
4月前
|
存储 监控 安全
一天十道Java面试题----第三天(对线程安全的理解------>线程池中阻塞队列的作用)
这篇文章是Java面试第三天的笔记,讨论了线程安全、Thread与Runnable的区别、守护线程、ThreadLocal原理及内存泄漏问题、并发并行串行的概念、并发三大特性、线程池的使用原因和解释、线程池处理流程,以及线程池中阻塞队列的作用和设计考虑。
|
1天前
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
7月前
|
缓存 安全 Java
7张图带你轻松理解Java 线程安全,java缓存机制面试
7张图带你轻松理解Java 线程安全,java缓存机制面试
|
4月前
|
Java
【Java集合类面试十二】、HashMap为什么线程不安全?
HashMap在并发环境下执行put操作可能导致循环链表的形成,进而引起死循环,因而它是线程不安全的。
|
4月前
|
安全 算法 Java
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
这篇文章讨论了Java集合类的线程安全性,列举了线程不安全的集合类(如HashSet、ArrayList、HashMap)和线程安全的集合类(如Vector、Hashtable),同时介绍了Java 5之后提供的java.util.concurrent包中的高效并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList。
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
|
5月前
|
设计模式 安全 NoSQL
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
74 0
|
5月前
|
安全 Java 调度
Java面试题:Java内存优化、多线程安全与并发框架实战,如何在Java应用中实现内存优化?在多线程环境下,如何保证数据的线程安全?使用Java并发工具包中的哪些工具可以帮助解决并发问题?
Java面试题:Java内存优化、多线程安全与并发框架实战,如何在Java应用中实现内存优化?在多线程环境下,如何保证数据的线程安全?使用Java并发工具包中的哪些工具可以帮助解决并发问题?
73 0
|
7月前
|
监控 安全 Java
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
128 1
|
7月前
|
SQL 存储 安全
Python安全编程面试:常见安全漏洞与防范措施
【4月更文挑战第19天】本文介绍了Python安全编程面试中的关键点,包括SQL注入、XSS攻击、命令注入、认证授权问题和密码安全。强调了理解安全漏洞原理、识别易受攻击的代码及采取防范措施的重要性。例如,使用参数化查询防止SQL注入,对用户输入进行HTML转义以防御XSS,通过列表形式传递命令参数避免命令注入,妥善管理认证凭据和采用强密码哈希策略。掌握这些知识能提升面试者的安全编程能力。
115 2

热门文章

最新文章