6.定时器和线程池

目录

一、定时器

1. Java.util包下的Timer类---“闹钟”

1.1 schedule(TimeTask task,long delay)方法

1.2 schedule(TimeTask task,long delay,long period)方法 

2. Java.util包下的TimerTask抽象类---指定需要执行的任务

3.实现一个简易的定时器---只执行一次

3.1MyTimer类 

3.2MyTimerTask类

3.3测试1 

3.4测试2

3.5改正测试2 

二、Thread.sleep()和wait(long timeout)的区别

三、线程池


一、定时器

定时器可以间隔特定的时间,用来执行特定的程序

定时器执行任务时,不会占用我们当前的执行流

1. Java.util包下的Timer类---“闹钟”

1.1 schedule(TimeTask task,long delay)方法

等到delay毫秒后执行一次任务

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

/**
 * @author happy
 */
public class UseTimer {
    public static void main(String[] args) throws InterruptedException {
    Timer timer = new Timer();      // 闹钟
    TimerTask task = new TimerTask() {  // 闹钟到时间后要做的任务
        @Override
        public void run() {
            System.out.println("闹钟响了"); //输出“闹钟响了”
        }
    };

    timer.schedule(task, 5000);

    while (true) {} // 主线程死循环,所以之后的打印,一定不是主线程打印的
}
}

1.2 schedule(TimeTask task,long delay,long period)方法 

周期性地执行任务,delay毫秒后,每过period毫秒执行一次任务

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

/**
 * @author happy
 */
public class UseTimer {
    public static void main(String[] args) throws InterruptedException {
        Timer timer = new Timer();      // 闹钟
        TimerTask task = new TimerTask() {  // 闹钟到时间后要做的任务
            @Override
            public void run() {
                System.out.println("闹钟响了");
            }
        };
        // 1秒之后,每隔2秒打印一次“闹钟响了”
        timer.scheduleAtFixedRate(task, 1000, 2000);

        while (true) {
        } // 主线程死循环,所以之后的打印,一定不是主线程打印的
    }
}

2. Java.util包下的TimerTask抽象类---指定需要执行的任务

 3.实现一个简易的定时器---只执行一次

写一个类似schedule(TimeTask task,long delay)方法

3.1MyTimer类 

import java.util.concurrent.PriorityBlockingQueue;

/**
 * @author happy
 */
public class MyTimer {
    // 这里是普通属性,不是静态属性
    private final PriorityBlockingQueue<MyTimerTask> queue = new PriorityBlockingQueue<>();

    public MyTimer() {
        Worker worker = new Worker();
        worker.start();
    }

    // 不能使用静态内部类,否则看不到外部类的属性
    class Worker extends Thread {
        @Override
        public void run() {
            while (true) {
                MyTimerTask task = null;
                try {
                    task = queue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // task 应该有个应该执行的时刻(不能记录 delay)
                long now = System.currentTimeMillis();
                // now是现在的时间
                // task.runAt是需要在什么时刻执行的时间
                // delay是需要休眠多长时间
                long delay = task.runAt - now;
                if (delay <= 0) {
                    task.run();
                } else {
                    try {
                        Thread.sleep(delay);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    task.run();
                }
            }
        }
    }

    public void schedule(MyTimerTask task, long delay) {
        // 该方法非工作线程调用
        task.runAt = System.currentTimeMillis() + delay;
        queue.put(task);
    }
}

3.2MyTimerTask类

/**
 * @author happy
 */
public abstract class MyTimerTask implements Comparable<MyTimerTask> {
    // 优先级队列,要求元素具备比较能力
    // runAt越小说明应该越先执行
    long runAt;     // 这个任务应该在何时运行(记录为 ms 为单位的时间戳)

    abstract public void run();


    @Override
    public int compareTo(MyTimerTask o) {
        if (runAt < o.runAt) {
            return -1;
        } else if (runAt > o.runAt) {
            return 1;
        } else {
            return 0;
        }
    }
}

3.3测试1 

public class Main {
    public static void main(String[] args) throws InterruptedException {
        MyTimerTask t1 = new MyTimerTask() {
            @Override
            public void run() {
                System.out.println("5s 之后");
            }
        };

        MyTimerTask t2 = new MyTimerTask() {
            @Override
            public void run() {
                System.out.println("4s 之后");
            }
        };

        MyTimerTask t3 = new MyTimerTask() {
            @Override
            public void run() {
                System.out.println("3s 之后");
            }
        };

        MyTimerTask t4 = new MyTimerTask() {
            @Override
            public void run() {
                System.out.println("2s 之后");
            }
        };

        MyTimerTask t5 = new MyTimerTask() {
            @Override
            public void run() {
                System.out.println("1s 之后");
            }
        };


        MyTimer myTimer = new MyTimer();
        myTimer.schedule(t1, 5000);
        myTimer.schedule(t2, 4000);
        myTimer.schedule(t3, 3000);
        myTimer.schedule(t4, 2000);
        myTimer.schedule(t5, 1000);

    }
}

输出结果

3.4测试2

public class Main {
    public static void main(String[] args) throws InterruptedException {
        MyTimerTask t1 = new MyTimerTask() {
            @Override
            public void run() {
                System.out.println("5s 之后");
            }
        };

        MyTimerTask t2 = new MyTimerTask() {
            @Override
            public void run() {
                System.out.println("1.5s 之后");
            }
        };

        MyTimer myTimer = new MyTimer();
        myTimer.schedule(t1, 5000);

        TimeUnit.SECONDS.sleep(1);
        myTimer.schedule(t2, 1500);

    }
}

 输出结果

 此时出错的原因是t1会休眠5s,在t1休眠期间t1是不知道t2只要休眠1.5s,导致t1执行完之后才会发现t2

3.5改正测试2 

不用Thread.sleep(),改用带超时的wait()

import java.util.concurrent.PriorityBlockingQueue;

/**
 * @author happy
 */
public class MyTimer {
    // 这里是普通属性,不是静态属性
    private final PriorityBlockingQueue<MyTimerTask> queue = new PriorityBlockingQueue<>();
    private final Object newTaskComing = new Object();


    public MyTimer() {
        Worker worker = new Worker();
        worker.start();
    }

    // 不能使用静态内部类,否则看不到外部类的属性
    class Worker extends Thread {
        @Override
        public void run() {
            while (true) {
                MyTimerTask task = null;
                try {
                    task = queue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // task 应该有个应该执行的时刻(不能记录 delay)
                long now = System.currentTimeMillis();
                // now是现在的时间
                // task.runAt是需要在什么时刻执行的时间
                // delay是需要休眠多长时间
                long delay = task.runAt - now;
                if (delay <= 0) {
                    task.run();
                }else {
                    try {
                        // 应该在两种条件下醒来:
                        // 1. 有新的任务过来了(任务可能比当前最小的任务更靠前)
                        // 2. 没有新任务来,但到了该执行该任务的时候了
                        synchronized (newTaskComing) {
                            newTaskComing.wait(delay);
                        }

                        // 如果发现新任务需要执行的时间在当前时间之前
                        // 说明需要先执行新任务
                        // 如果不是,就先把新任务放回到队列中,重新取最小的任务
                        if (System.currentTimeMillis() >= task.runAt) {
                            task.run();
                        } else {
                            queue.put(task);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public void schedule(MyTimerTask task, long delay) {
        // 该方法非工作线程调用
        task.runAt = System.currentTimeMillis() + delay;
        queue.put(task);
        synchronized (newTaskComing) {
            newTaskComing.notify();
        }
    }
}

运行结果

二、Thread.sleep()和wait(long timeout)的区别

1.语义不同;一个是休眠,一个是等待

2. sleep()是有固定的休眠时间的,wait(timeout)可能 <= 休眠时间;wait(timeout)有两个结束条件:超时时间

3.sleep()是 Thread 的静态方法wait(timeout) 是 Object 的方法

4.wait 需要搭配 synchronized 使用;sleep 不需要

三、线程池

  • 标准库的线程池---ThreadPoolExecutor
  • 因为创建/销毁一个线程都是有成本的
  • 线程池是按照需求提前创建很多个线程,一旦有一个新的任务,就交给储备的线程来处理有四个构造函数

对于第四个构造方法,假设我们把线程池看成一个公司

  1. 刚开始的时候,线程池里没有工作线程
  2. 当线程数量 <  maximumPoolSize,之间创建一个新的正式员工
  3. 当线程数量 =  maximumPoolSize,暂时把任务放到队列
  4. 当线程到达上限而且队列也满了的时候,就会执行拒绝策略

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习java的张三

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值