Written by
Yitong Huang
on
on
Java高并发 - 22.Balking设计模式
多个线程监控某个共享变量,A线程监控到共享变量发生变化后即将触发某个动作,但是此时发现有另外一个线程B已经针对该变量的变化开始了行动,A便放弃了准备开始的工作。我们把这样的线程间交互称为Balking(犹豫)设计模式。
比如,我们在用word编写文档的时候,每次的文字编辑都代表着文档的状态发生了变化,除了我们可以手动保存为,word本身也会定期触发自动保存。如果word自动保存文档的线程在准备执行保存动作的时候,恰巧我们进行了主动保存,那么自动保存文档的线程将会放弃此次保存操作。
Document
Document类代表文档本身,有save和edit两个主要方法用于保存和编辑文档。
// 代表正在编辑的文档类
public class Document {
// 如果文档发生改变,changed会被设置为true
private boolean changed = false;
// 一次需要保存的内容,可以将其理解为内容缓存
private List<String> content = new ArrayList<>();
private final FileWriter writer;
// 自动保存文档的线程
private static AutoSaveThread autoSaveThread;
// 构造函数需要传入文档保存的路径和文档名称
private Document(String documentPath, String documentName)
throws IOException {
this.writer = new FileWriter(new File(documentPath, documentName));
}
// 静态方法,主要用于创建文档,顺便启动自动保存文档的线程
public static Document create(String documentPath, String documentName)
throws IOExceptioin {
Document document = new Document(documentPath, documentName);
autoSaveThread = new AutoSaveThread(document);
autoSaveThread.start();
return document;
}
// 编辑文档,其实就是往content队列中提交字符串
public void edit(String content) {
synchronized(this) {
this.content.add(content);
// 文档改变,changed会变为true
this.changed = true;
}
}
// 文档关闭的时候首先中断自动保护线程,然后关闭writer释放资源
public void close() throws IOException {
autoSaveThread.interrupt();
writer.close();
}
// 显式保存文档
public void save() throws IOException {
synchronized(this) {
// balking, 如果文档已经被保存了,则直接返回
if (!changed) {
return;
}
System.out.println(currentThread() + " execute the save action");
// 将内容写入文档
for (String cacheLine : content) {
this.writer.write(cacheLine);
this,writer.write("\r\n");
}
this.writer.flush();
// 将changed修改为false,表明没有新的缓存内容
this.changed = false;
this.content.clear();
}
}
}
AutoSaveThread
AutoSaveThread用于定时保存文档。
public class AutoSaveThread extends Thread {
private final Document document;
public AutoSaveThread(Document document) {
super("DocumentAutoSaveThread");
this.document = document;
}
@Override
public void run() {
while(true) {
try {
// 每隔一秒自动保存一次文档
document.save();
TimeUnit.SECONDS.sleep(1);
} catch(IOException | InterruptedException e) {
break;
}
}
}
}
总结
Balking模式在日常开发中很常见,比如系统资源的加载或者某些数据的初始化。在整个系统生命周期中资源可能只被加载一次,可以采用balking模式加以解决。
public synchronized Map<String, Resource> load() {
// balking
if (loaded) {
return resourceMap;
} else {
// do the resource load task
// ...
this.loaded = true;
return resourceMap;
}
}