在多线程编程时,我们需要考虑到资源的有效利用和线程占有资源问题。
例如在C-S框架中,客户机连接服务器的操作。当客户机连接到服务器时,如果长时间没有进行信息交互而一直保持与服务器的连接,这不仅会增加服务器的负担,还会造成CPU资源的浪费。因此,我们完成一个计时器工具,检测当客户机在指定时间内有没有进行操作,如果没有则可以通过调用抽象方法doSomething()来对客户机或相关线程进行关闭或放在缓冲区中等操作,及时归还CPU资源;而当客户机需要进行信息交互时,重新向服务器发送连接请求。
在代码实现上必须要考虑锁和计时器的误差。
我们需要创建两个线程,第一个为本类Didadida下的线程,该线程用来在指定时间内唤醒doSomething()操作;第二个线程作为一个调用业务函数(在计时器时间之外)的线程,其通过内部类来实现,并且刚创建即阻塞,当第一个线程执行notify()时,唤醒该线程并执行doSomething()方法。
执行这两个线程时必须使用同一个对象锁,并且将线程的阻塞和唤醒作为临界资源避免其他线程抢占。
源代码:
package com.chy.timer.core;
public abstract class Didadida implements Runnable {
private volatile boolean goon = false;
private Object lock;
private long delayTime;
private static final long defaultTime = 1000;
public Didadida() {
this.lock = new Object();
}
public abstract void doSomething();
public Didadida setDelayTime(long delayTime) {
this.delayTime = delayTime;
return this;
}
public void start() {
if (goon == true) {
return;
}
goon = true;
new DidadidaWoker();
Thread thread = new Thread(this, "com.example.bootDemo.bootstrap.common.Didadida");
// 设置保护线程,主线程结束了,保护线程也就结束了
thread.setDaemon(true);
thread.start();
}
@Override
public void run() {
if (delayTime == 0) {
delayTime = defaultTime;
}
synchronized (lock) {
try {
// 指定时间结束后唤醒阻塞线程;
// 若业务函数所在线程没有在指定时间内完成业务并结束线程;
// 则本线程的notify()为无效操作,进入下一时间片段。
lock.wait(delayTime);
lock.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("守护线程" + Thread.currentThread().getName());
}
}
public class DidadidaWoker implements Runnable{
public DidadidaWoker() {
new Thread(this).start();
}
@Override
public void run() {
while (goon) {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
doSomething();
// 业务线程执行完,结束线程,不然会一直在运行
goon = false;
}
}
}
// 扩展了抽象类
public class DidadidaImpl extends Didadida {
@Override
public void doSomething() {
System.out.println("业务线程" + Thread.currentThread().getName());
}
}