滴答滴答定时器实现(多线程模式和锁的应用)

定时器的作用:

        不断循环指定的时间间隔,并且在时间间隔结束后能够执行用户指定的任务。

需求:

        1、允许用户在指定时间间隔;

        2、允许用户指定任务;

        3、尽可能做到“精确定时”,消除误差;

        4、将定时器实现成为工具;

首先,设置delayTime = 1000, Task任务为输出时间,接着使用Runnable接口的方式创建定时器主线程,主线程需要做的工作就是不断地休眠delayTime时长,并且在每次休眠结束时完成指定任务Task。

public class Test implements Runnable{
	
	private boolean goon = true;
	private int delayTime = 1000;
	
	public Test() {
	}
	
	public  void start() {
		new Thread(this).start();
	}
	
	@Override
	public void run() {
		while(goon) {
			try {
				Thread.sleep(delayTime);
				Task.run();
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
		}
	}
	
	public static class Task {
		public static void run() {
			System.out.println(System.currentTimeMillis());
		}
	}
}

 如此做确实能够完成定时器的任务,但是定时器每次经过delayTime后还需要执行完成任务后才会进入一轮循环,从而产生时间误差。

时间间隔 = delayTime + 执行Task的时间;

为了消除这一误差,我们可以将需要执行的任务也制定为一个独立的线程,并且为任务线程上锁,使其进入阻塞态。当我们启动定时器主线程后,主线程先休眠delayTime,然后只需要打开任务线程的锁即可。

时间间隔 = delayTime + 启动Task的时间 

 同时为了线程的安全性,我们给主线程也加上锁,使两个线程不会冲突,并且在启动时,会先将任务线程预加载,随之上锁。

运行过程: 定时器启动  ---> 预加载任务线程 --> 任务线程进入阻塞态 --> 启动主线程 

--> 休眠DelayTime -- > 唤醒任务线程、随即主线程进入阻塞态 --> 任务线程执行任务

在应用synchronized锁时,有一个因失误而造成的错误,使得在休眠后,Task任务只会被执行一次。

@Override	//定时器主线程
	public void run() {
		
		while(goon) {
			synchronized (lock) {
				lock.notify();	
				try {
					Thread.sleep(delayTime);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

这是因为我将sleep休眠放在了synchronized锁当中,而同一时间、同一把锁只能执行一个锁中的内容,致使在唤醒任务进程时,并没有进行“计时”,正确的做法应当时将sleep放置在锁外。从这一点中我们也可以看出synchronized锁所具有的特性。

完整代码:


public class Didadida implements Runnable{
	private  int delayTime = 1000;
	private Runnable task;
	private volatile boolean goon = false;
	Object lock = new Object();
	
	public Didadida() {
		
	}
	public Didadida(Runnable task) {
		setTask(task);
	}
	public Didadida(int delayTime) {
		setDelayTime(delayTime);
	}
	public Didadida(int delayTime, Runnable task) {
		setTask(task);
		setDelayTime(delayTime);
	}
	
	//设置间隔时间;
	 public void setDelayTime(int delayTime) {
		 this.delayTime = delayTime;
	 }
	 
	 //设置任务;
	 public void setTask(Runnable task) {
		 this.task = task;
	 }
	 
	 //启动程序
	 public void startUp() throws InterruptedException {
		 if(goon) {
			 return;
		 }
		 this.goon = true;
		 if(task == null) {
			 System.out.println("任务为空,无法启动线程......");
			 return;
		 }
			synchronized (lock) {
				new Thread(new StartTask()).start();
				lock.wait();
			}
			new Thread(this, "定时器").start();
	 }
	 
	 public void end() {
		 this.goon =false;
	 }
	 
	 
	@Override	//定时器主线程
	public void run() {
		
		while(goon) {
			
			synchronized (lock) {
				lock.notify();	
				try {
					Thread.sleep(delayTime);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	class StartTask implements Runnable{
		public StartTask(){	
			new Thread(this).start();
		}
		
		@Override	//任务线程
		public void run() {		
			synchronized (lock) {
				lock.notify();
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			while(goon) {
				synchronized (lock) {
					try {
						task.run();
						lock.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
				}
			}
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值