Java计时器

Java计时器

用途

使用线程实现Java计时器,主要用于根据用户给定的时间,然后隔一段时间做一些事。例如隔一段时间刷新一些列表等。然而在Executor框架里,有一个Executor的实现类ScheduledThreadPoolExecutor类,可以在给定的延迟后运行命令,或者定期执行命令,比Timer更灵活,功能更强大。

实现过程

注意sleep();和wait();方法
  1. sleep()方法:调用后该线程睡眠给定的时间,但是没有释放锁,一直持有锁。
  2. wait()方法:调用后该线程进入阻塞队列,同时释放锁。当被唤醒后再一次重新争夺锁。
  3. 在这里用的是wait()方法,因为sleep()方法在睡眠的时候一直占用锁资源,也就是说,sleep睡眠时间没有到,其它线程就得不到锁,无法进入就绪态,得不到CPU的使用权。因此使用sleep()方法会浪费CPU的资源,所以这里使用wait()方法。
方案一

1.首先定义一个抽象方法,这方法由外面实例化该类的使用者完成。
2.然后启动一个线程,使用wait()方法将该线程阻塞指定的时间,然后的调用该抽象方法。

代码展示
方案二

1.首先定义一个抽象方法,这方法由外面实例化该类的使用者完成。
2. 然后启动两个线程,一个线程用于计时(主要是根据给定的时间阻塞线程,阻塞时间到后唤醒另一个完成具体工作的线程),一个线程用于完成用户指定的工作(该线程主要是调用那个待完成的抽象方法)。

代码展示
//因为篇幅问题,这里只展示核心代码,其余代码和方案二相同。
@Override
	public void run() {
			try {
				while(goon) {
					synchronized (lock) {
				       //阻塞dealyTime毫秒
				        lock.wait(dealyTime);
					}
					//当阻塞时间到线程被唤醒后直接调用抽象类完成用户指定的工作
					work();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
	}
//main方法
public static void main(String[] args) {
		new Didadida() {
			@Override
			public void work() {
				int n = 0;
				//这里增加了for循环,为了增加该方法的执行时间,只是为了验证
				for (int i = 0; i < 1000000000; i++) {
					n += i;
				}
				System.out.println(System.currentTimeMillis());
			}
		}.start();
	}
运行结果


这里的输出结果是以毫秒为单位的,这段代码验证的是阻塞1秒的结果

//实现类
package com.mec.didadida.core;

public abstract class Didadida implements Runnable {
	private Object lock;
	private volatile boolean goon;
	private long dealyTime;
	
	//给定一个默认时间值
	private static final int DEFULETIME = 1000;
	
	public Didadida() {
		this.lock = new Object();
		this.dealyTime = DEFULETIME;
		this.goon=false;
	}

    //由外面设置延迟时间	
	public Didadida setDealyTime(long dealyTime) {
		this.dealyTime = dealyTime;
		return this;
	}
	
	//给定一个抽象方法,有外面实例化该类的用户实现具体的完成工作
	public abstract void work();
	
	//启动线程
	public void startThread() {
		如果线程已经启动,直接结束方法的调用
		if (goon) {
			return;
		}
		goon = true;
		//启动工作线程
		new Thread(new Action(), "工作").start();
		//启动定时线程
		new Thread(this, "计时器").start();
		
	}
	
	//结束线程
	public void stop() {
		if (!goon) {
			return;
		}
		goon = false;
	}
	
	@Override
	public void run() {
			try {
				while(goon) {
					synchronized (lock) {
				        //将该线程阻塞给定的时间,单位毫秒
				        lock.wait(dealyTime);
				        //唤醒工作线程
				        lock.notify();
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
	}
	//定义一个内部类,完成具体的工作
	class Action implements Runnable {

		@Override
		public void run() {
			try {
				while(goon) {
					synchronized (lock) {
				        //阻塞工作线程
				        lock.wait();
					}
					//调用抽象
					work();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}

}

//主类
package com.mec.didadida.test;

import com.mec.didadida.core.Didadida;

public class DidaTest {

	public static void main(String[] args) {
		//实例化类并且完成该抽象类的抽象方法
		new Didadida() {
			@Override
			public void work() {
				int n = 0;
				//这里增加了for循环,为了增加该方法的执行时间,只是为了验证
				for (int i = 0; i < 1000000000; i++) {
					n += i;
				}
				System.out.println(System.currentTimeMillis());
			}
		}. startThread(); //调用启动线程方法
	}

}
运行结果

在这里插入图片描述
这是给定时间为1秒所输出的结果,结果是当前时间用毫秒表示。

总结

方案一:

  1. 该方法使用一个线程完成计时工作,当线程阻塞指定时间后直接执行由用户实现的抽象方法。
  2. 假如该线程被唤醒,进入就绪态,如果work()方法执行时间过长,该线程就会在work()方法执行完后才能循环回去执行wait()方法,重新阻塞该线程指定的时间,这样用户的定时就不准确了,真正的时间是:线程阻塞时间 + work()方法执行的时间,所以会有误差。
  3. 为了解决这个问题,提出了方案二的解决办法。

方案二:

  1. 该方案提供两个线程来解决问题,一个问题负责计时,一个线程负责完成工作。
  2. 首先,当Didadida线程阻塞时间到被唤醒后执行notify()方法,唤醒Action线程,然后循环回去继续执行wait()方法,阻塞指定的时间,等到时间到了在重复此操作。Action线程被唤醒后执行work()方法,该方法执行完后马上回去继续阻塞等待下一次被唤醒。
不足

上述两种方案都存在一个不足,那就是具体完成任务的work()方法不能太复杂,也就是执行时间不能过长,如果执行时间过长,都会对调用该方法的线程有所延迟,产生误差!

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值