监听者
/**
* @author 12130
* @date 2019/11/26
* @time 22:35
*/
public interface LifeCycleListener {
// 当出现某些事件的时候,通知监听者
void onEvent(ObservableRunnable.RunnableEvent event);
}
监听者的实现类
/**
* @author 12130
* @date 2019/11/26
* @time 22:39
* <p>
* 监听者的实现类,监听任务的执行情况
*/
public class ThreadLifeCycleObserver implements LifeCycleListener {
private final Object LOCK = new Object();
@Override
public void onEvent(ObservableRunnable.RunnableEvent event) {
synchronized (LOCK) {
System.out.println("The runnable (" + event.getThread().getName() + ") date changed and state is" + event.getState());
if (event.getCause() != null) {
System.out.println("The runnable (" + event.getThread().getName() + ") process fail");
event.getCause().printStackTrace();
}
}
}
}
被监听者
package E_观察者模式观察线程的声明周期;
/**
* @author 12130
* @date 2019/11/26
* @time 22:34
* <p>
* 任务的包装类,因为我这里只监听任务的开始,结束,异常三种状态,所以直接写一个包装类来包装我们的任务
* 复用代码的同时也避免了对我们任务类的代码侵入。
* 但是如果你想监听一个任务具体的执行的中间过程的话,这样就不行了,就必须要在run()方法里面在任务的时刻通知监听者
* <p>
* 比如现在你写了一个爬虫程序,有这个爬虫程序执行有三个时期
* 1、获取url
* 2、爬取数据
* 3、数据分析
* 很明显这种情况下就必须要对在run()方法中在每个状态的结束主动的通知监听者类,也就是说必须要有对任务类的代码侵入。
* 或者说自定义一个任务类,在任务类中把任务划分,去监听执行的过程
*/
public final class ObservableRunnable implements Runnable {
/**
* 监听者
*/
protected final LifeCycleListener listener;
/**
* 包装的任务类
*/
private final Runnable task;
public ObservableRunnable(LifeCycleListener listener, Runnable task) {
this.listener = listener;
this.task = task;
}
@Override
public void run() {
try {
// 通知任务开始执行
notifyChange(new RunnableEvent(RunnableState.RUNTIME, Thread.currentThread(), null));
task.run();
// 任务执行结束
notifyChange(new RunnableEvent(RunnableState.DONE, Thread.currentThread(), null));
} catch (Throwable e) {
// 任务执行错误
notifyChange(new RunnableEvent(RunnableState.ERROR, Thread.currentThread(), e));
}
}
protected void notifyChange(final RunnableEvent event) {
listener.onEvent(event);
}
public enum RunnableState {
/**
* 任务执行中
*/
RUNTIME,
/**
* 任务执行错误
*/
ERROR,
/**
* 任务执行结束
*/
DONE
}
public static class RunnableEvent {
private final RunnableState state;
private final Thread thread;
private final Throwable cause;
public RunnableEvent(RunnableState state, Thread thread, Throwable cause) {
this.state = state;
this.thread = thread;
this.cause = cause;
}
public RunnableState getState() {
return state;
}
public Thread getThread() {
return thread;
}
public Throwable getCause() {
return cause;
}
}
}
测试类
import java.util.Arrays;
import java.util.List;
/**
* @author 12130
* @date 2019/11/26
* @time 22:47
*/
public class ThreadLifeCycleClient {
public static void main(String[] args) {
List<String> ids = Arrays.asList("1", "2", "3", "4");
ThreadLifeCycleObserver observer = new ThreadLifeCycleObserver();
// 将任务使用我们的包装类进行包装
ids.forEach(name -> new Thread(new ObservableRunnable(observer, () -> {
System.out.println("当前任务正在执行一些操作");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}), name).start());
}
}
观察者执行的过程:
实现观察者模式有很多形式,比较直观的一种是使用一种“注册—通知—撤销注册”的形式。
观察者
(Observer)将自己注册到被观察对象(Subject)中,被观察对象将观察者存放在一个容器(Container)里。
被观察
被观察对象发生了某种变化(如图中的SomeChange),从容器中得到所有注册过的观察者,将变化通知观察者。
撤销观察
观察者告诉被观察者要撤销观察,被观察者从容器中将观察者去除。观察者将自己注册到被观察者的容器中时,被观察者不应该过问观察者的具体类型,而是应该使用观察者的接口。这样的优点是:假定程序中还有别的观察者,那么只要这个观察者也是相同的接口实现即可。一个被观察者可以对应多个观察者,当被观察者发生变化的时候,他可以将消息一一通知给所有的观察者。基于接口,而不是具体的实现,这一点为程序提供了更大的灵活性。