【JavaEE】定时器的简单实现

目录

定时器

实现定时器

描述任务

保存任务

扫描任务

执行任务


定时器

在实现定时器之前,先来简单的了解一下什么是定时器。

定时器是软件开发中一个重要的组件。比如到了什么时候,干一件什么样的事情;多少秒之后干什么。本篇文章介绍的多长时间后干什么事情。

Java标准库中的定时器,所提供的类——Timer  核心方法是schedule()。这里只演示多久之后执行任务这个方法。

import java.util.Timer;
import java.util.TimerTask;

public class ThreadDemo25 {

    public static void main(String[] args) {

        Timer timer = new Timer();

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("这是任务1, 1000ms后执行");
            }
        }, 1000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("这是任务2, 2000ms后执行");
            }
        }, 2000);

        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println("这是任务3,3000ms后执行");
            }
        };
        timer.schedule(task, 3000);
    }

}

这里schedule中的两个参数

一个是TimeTask task  描述具体要执行的任务

一个是long time  描述time时间后执行该任务

其中TimeTask是一个抽象类,实现了Runnable接口。可以简单的理解为是堆Runnable接口的升级,实际上还是Runnable接口。


实现定时器

实现的是多久之后执行任务这一定时器。

描述任务

这里需要一个类是描述这个任务,因为除了任务之外,还需要时间。

// 使用Task类描述任务
// 因为存储该任务使用的是优先级阻塞队列,要比出优先级需要有比较器
// 这里直接实现Comparable接口
class Task implements Comparable<Task> {
    // 时间
    private long after;
    // 任务
    private Runnable runnable;

    public Task(Runnable runnable, long after) {
        this.runnable = runnable;
        this.after = after;
    }

    @Override
    public int compareTo(Task task) {
        return (int)(this.after - task.after);
    }

    public long getAfter() {
        return after;
    }

    public void run(){
        this.runnable.run();
    }
}

保存任务

这里使用PriorityBlockingQueue来保存任务。因为保存进去的时候会按照时间来保存。带有优先级的阻塞队列完美符合这一点。检查是否要执行任务,只需要查看队首的元素时间是不是到了。

    // 使用优先级阻塞队列保存任务
    private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();

扫描任务

定时器创建时(所以需要在构造方法中),需要一个线程一直扫描检查里面的任务时间是否到了,到了就立马执行。

    // 扫描线程时间
    private Thread scan = null;

执行任务

执行任务根据时间来判断是否要执行。如果时间还没到的时候,那么线程应该阻塞才好。其中时间是不固定的。可能随时会有新的任务添加进来,这样就会导致时间的不确定性。这里使用wait和notify来让任务阻塞还是执行。如果有新的任务添加进来,就唤醒以下正在阻塞的扫描线程,看看新的时间是否要执行,如果时间还不到,就继续阻塞,知道最早的时间到了,在执行任务。

    public MyTimer() {
        // 创建一个线程扫描任务时间
        scan = new Thread(() -> {
            while (true) {
                try {
                    long now = System.currentTimeMillis();
                    Task task = queue.take();
                    // 如果最快执行的任务的时间大于当前时间,就wait二者相差时间
                    // 否则将会一直取出来在放进去
                    if (task.getAfter() > now) {
                        queue.put(task);
                        synchronized (locker) {
                            locker.wait(task.getAfter() - now);
                        }
                    } else {
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        scan.start();
    }

    public void schedule (Runnable runnable, long after){
        Task task = new Task(runnable, System.currentTimeMillis() + after);
        queue.put(task);
        synchronized (locker) {
            locker.notify();
        }
    }

完整代码如下

// 实现一个定时器

import java.util.concurrent.PriorityBlockingQueue;

// 使用Task类描述任务
// 因为存储该任务使用的是优先级阻塞队列,要比出优先级需要有比较器
// 这里直接实现Comparable接口
class Task implements Comparable<Task> {
    // 时间
    private long after;
    // 任务
    private Runnable runnable;

    public Task(Runnable runnable, long after) {
        this.runnable = runnable;
        this.after = after;
    }

    @Override
    public int compareTo(Task task) {
        return (int)(this.after - task.after);
    }

    public long getAfter() {
        return after;
    }

    public void run(){
        this.runnable.run();
    }
}

class MyTimer {
    // 扫描线程时间
    private Thread scan = null;

    // 使用优先级阻塞队列保存任务
    private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();

    // 时间还没有到的时候
    // 使用这个对象进行加锁和唤醒
    private Object locker = new Object();

    // 构造方法
    public MyTimer() {
        // 创建一个线程扫描任务时间
        scan = new Thread(() -> {
            while (true) {
                try {
                    long now = System.currentTimeMillis();
                    Task task = queue.take();
                    // 如果最快执行的任务的时间大于当前时间,就wait二者相差时间
                    // 否则将会一直取出来在放进去
                    if (task.getAfter() > now) {
                        queue.put(task);
                        synchronized (locker) {
                            locker.wait(task.getAfter() - now);
                        }
                    } else {
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        scan.start();
    }

    public void schedule (Runnable runnable, long after){
        Task task = new Task(runnable, System.currentTimeMillis() + after);
        queue.put(task);
        synchronized (locker) {
            locker.notify();
        }
    }
}

public class ThreadDemo26 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("这是任务1");
            }
        }, 6000);

        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("这是任务2");
            }
        }, 2000);
    }
}

 


有什么问题评论区指出。希望可以帮到你。 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用JavaEE的Servlet技术来实现简易计算器。以下是一个简单的例子: 1. 创建一个名为CalculatorServlet的Java类,继承HttpServlet类。 2. 在doGet和doPost方法中,获取用户输入的操作数和操作符,并进行计算。 3. 将计算结果返回给用户。 下面是一个示例代码: ```java import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class CalculatorServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int num1 = Integer.parseInt(request.getParameter("num1")); int num2 = Integer.parseInt(request.getParameter("num2")); String operator = request.getParameter("operator"); int result = 0; switch (operator) { case "+": result = num1 + num2; break; case "-": result = num1 - num2; break; case "*": result = num1 * num2; break; case "/": result = num1 / num2; break; default: break; } response.getWriter().write("Result: " + result); } } ``` 在Web.xml文件中添加Servlet配置: ```xml <servlet> <servlet-name>CalculatorServlet</servlet-name> <servlet-class>CalculatorServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CalculatorServlet</servlet-name> <url-pattern>/calculator</url-pattern> </servlet-mapping> ``` 在JSP页面中添加表单: ```html <form action="calculator" method="post"> <input type="text" name="num1"> <select name="operator"> <option value="+">+</option> <option value="-">-</option> <option value="*">*</option> <option value="/">/</option> </select> <input type="text" name="num2"> <input type="submit" value="Calculate"> </form> ``` 这样,用户就可以在浏览器中访问该JSP页面,并使用简易计算器进行计算了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值