定时器滴答滴答(synchronized实现 以及 Lock实现)

在一些应用场合中,我们需要用到精准定时,比如每过一段时间,就执行固定一段代码。今天就来介绍滴答滴答定时器。


初步的想法是构建一个线程,给一个开始或结束的判断标志,加到while循环的判断条件中,加上锁,使得线程安全。每隔一段时间就让线程进入等待(阻塞)状态—lock.wait(delay),等时间到了线程自动被唤醒进入就绪状态,然后我们执行我们想要执行的代码。

以下是SimpleDidaDida类代码

package com.mec.dida;

public abstract class SimpleDidaDida implements Runnable {
	public static final long DELAY = 1000;
	public long delay;
	public volatile boolean goon;
	private Object lock;
	
	public SimpleDidaDida() {
		this(DELAY);
	}
	
	public SimpleDidaDida(long delay) {
		this.lock = new Object();
		this.delay = delay;
	}
	
	public void start() {
		if(goon == true) {
			return;
		}
		goon = true;
		new Thread(this).start();
	}
	
	public void stop() {
		if(goon ==false) {
			return;
		}
		goon = false;
	}
	
	public abstract void doing();

	@Override
	public void run() {
		while(goon) {
			synchronized (lock) {
				try {
					lock.wait(delay);
					doing();
					System.out.println("时间:" + System.currentTimeMillis());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
		}
	}
	
	
	
}

测试Test代码

package com.mec.dida;

public class Test {

	private SimpleDidaDida dida;
	
	public Test() {
		dida = new SimpleDidaDida(1000) {
			
			@Override
			public void doing() {
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
			}
		};
		dida.start();
	}
	
	public static void main(String[] args) {
		Test test = new Test();
	}

}

测试结果如下:

我们可以看到实际线程并没有按照我们的设想,每个1秒执行一次doing(),因为执行doing()方法所用的时间也算入了我们的定时时间,这样可不行,就乱套了。


第二次设想:分析上述结果,我们发现只要将doing()方法放进循环里,就一定会占用一定的时间,但是它又必须在循环里,不然,这个定时器就毫无意义了!

所以我的想法是,构造一个实现了Runnable接口的内部类,我们将线程的启动直接放到内部类无参构造中,并在run()中执行doing()方法。

以下是DidaDida的代码

package com.mec.dida;

public abstract class DidaDida implements Runnable {
	public static final long DELAY = 1000;
	public long delay;
	public volatile boolean goon;
	private Object lock;
	
	public DidaDida() {
		this(DELAY);
	}
	
	public DidaDida(long delay) {
		this.lock = new Object();
		this.delay = delay;
	}
	
	public void start() {
		if(goon == true) {
			return;
		}
		goon = true;
		new Thread(this).start();
	}
	
	public void stop() {
		if(goon ==false) {
			return;
		}
		goon = false;
	}
	
	public abstract void doing();

	@Override
	public void run() {
		while(goon) {
			synchronized (lock) {
				try {
					lock.wait(delay);
					new DidaSon();
					System.out.println("时间:" + System.currentTimeMillis());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
		}
	}
	
	private class DidaSon implements Runnable {

		public DidaSon() {
			new Thread(this).start();
		}
		
		@Override
		public void run() {
			doing();
		}
		
	}
	
	
	
}

测试Test代码

package com.mec.dida;

public class Test {
	DidaDida dida;

	
	public Test() {
	
		dida = new DidaDida(1000) {
			
			@Override
			public void doing() {
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		dida.start();
	}
	
	public static void main(String[] args) {
		Test sim = new Test();
	}
	
	 
}

测试结果如下:

这个单位是以毫秒为单位的!可以看到即使我们在doing()方法中让线程进入阻塞态500ms,对于我们定时器的影响也只有0~2ms的延时差,因为毕竟我们也进行了初始化操作new DidaSon();


Lock实现

package com.mec.didadida.core;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public abstract class DidaDidaL implements Runnable {
	public static final long TIME_OUT = 3000;
	private Lock lock;
	private long timeout;
	private boolean goon;
	public Condition condition;
	private ThreadPoolExecutor threadPool;

	public DidaDidaL() {
		this(TIME_OUT);
	}
	
	public DidaDidaL(long timeout) {
		this.timeout = timeout;
		lock = new ReentrantLock();
		condition = lock.newCondition();
		threadPool = 
				new ThreadPoolExecutor(5, 7, TIME_OUT, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>());
	}
	
	public void setTimeout(long timeout) {
		this.timeout = timeout;
	}

	public void start() {
		if(!goon) {
			goon = true;
			new Thread(this).start();
		}
	}

	public void stop() {
		if(goon) {
			goon = false;
		}
	}
	
	public abstract void doing();
	
	@Override
	public void run() {
		while(goon) {
			lock.lock();
			try {
				condition.await(timeout, TimeUnit.MILLISECONDS);
				//threadPool.execute(new DidaDoing());
				new DidaDoing();
				
			
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				lock.unlock();
			}
			
		}
	}
	
	

	class DidaDoing implements Runnable {
		
		public DidaDoing() {
			new Thread(this).start();
		}

		@Override
		public void run() {
			doing();
		}
		
	}
}

到这里,我们的定时器滴答滴答的叙述完啦!

综上:这只是线程应用的一个小技巧,以后还有更多需要学习的内容!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值