什么是Balking设计模式?
多个线程监控某个共享变量,A线程监控到共享变量发生变化后即将触发某个动作,但是此时发现有另外一个线程B已经针对该变量的变化开始了行动,因此A便放弃了准备开始的动作,我们把这样的线程间交互成为Balking(犹豫)设计模式。其实这样的场景在生活中很常见,比如你去饭店吃饭,吃到途中想要再点一个小菜,于是你举起手示意服务员,其中一个服务员看到了你举手正准备走过来的时候,发现距离你 比较近的服务员已经准备要受理你的请求于是中途放弃了。
再比如,我们在用word编写文档的时候,每次的文字编辑都代表着文档的状态发生了改变,除了我们可以使用 ctrl+s快捷键手动保存以外,word软件本身也会定期触发自动保存,如果word自动保存文档的线程在准备执行保存 动作的时候,恰巧我们进行了主动保存,那么自动保存文档的线程将会放弃此次的保存动作。
看了以上两个例子的说明,想必大家已经清楚了Balking设计模式要解决的问题了吧,简短的说就是某个线程因为 发现其他线程正在进行相同的工作而放弃即将开始的任务。在本章中,我们将通过模拟word文档自动保存与手动保存的功能讲解 Balking模式的设计与应用。
Balking模式之文档编辑
Document:
package MutilThreadModel.BalkingModel;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static java.lang.Thread.currentThread;
/**
* Created by JYM on 2019/1/14
* 在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),true);
}
//静态方法,主要用于创建文档,顺便启动自动保存文档的线程
public static Document create(String documentPath,String documentName) throws IOException
{
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();
}
//save方法用于为外部显式进行文档保存
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();
}
}
}
/**在上面的代码中:
* 1>edit方法和save方法进行方法同步,其目的在于防止当文档在保存的过程中如果遇到新的内容被编辑时
* 引起的共享资源冲突问题。
* 2>changed在默认的情况下为false,当有新的内容被编辑的时候将被修改为true。
* 3>在进行文档保存的时候,首先查看changed是否为true,如果文档发生过编辑则在文档中保存新的内容,
* 否则就会放弃此次保存动作,changed是balking pattern关注的状态,当changed为true的时候就想远处的
* 服务员看到客户的请求被另外一个服务员接管了一样,于是放弃了任务的执行。
* 4>在创建Document的时候,顺便还会启动自动保存文档的线程,该线程的主要目的在于在固定时间里执行
* 一次文档保存动作。
* */
AutoSaveThread
package MutilThreadModel.BalkingModel; import java.io.IOException; import java.util.concurrent.TimeUnit; /** * Created by JYM on 2019/1/14 * 下面这个类函数实现的是文档的自动保存. * AutoSaveThread比较简单,其主要的工作就是每个一秒的时间调用一次document的save方法。 * */ 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; } } } }
DocumentEditThread:
package MutilThreadModel.BalkingModel; import java.io.IOException; import java.util.Scanner; /** * Created by JYM on 2019/1/14 * DocumentEditThread线程则类似于主动编辑文档的作者,在DocumentEditThreadzhong中除了对文档进行修改编辑之外,还是同时按下Ctrl+S * 组合键(调用save方法)主动保存。 * */ //该线程代表的是主动进行文件编辑的线程,为了增加交互性,我们使用了Scanner public class DocumentEditThread extends Thread { private final String documentPath; private final String documentName; private final Scanner scanner = new Scanner(System.in); public DocumentEditThread(String documentPath,String documentName) { super("DocumentEditThread"); this.documentPath = documentPath; this.documentName = documentName; } @Override public void run() { int times = 0; try { Document document = Document.create(documentPath,documentName); //创建编辑文档 while (true) { //获取用户的键盘输入 String text = scanner.next(); if ("quit".equals(text)) { document.close(); break; } //将内容编辑到document中 document.edit(text); if (times==5) { //用户在输入了5次之后进行文档保存 document.save(); times=0; } times++; } }catch (IOException e) { throw new RuntimeException(e); } } } /** * DocumentEditThread类代表了主动编辑文档的线程,在该线程中,我们使用scanner交互的方式,每一次对文档的修改 * 都不可能直接保存(Ctrl+S),因此在程序中约定了五次以后主动执行保存动作,当输入quit时,表示要退出此次文档编辑。 * */
测试:
package MutilThreadModel.BalkingModel; /** * Created by JYM on 2019/1/14 * * 测试程序: * */ public class BalkingTest { public static void main(String[] args) { new DocumentEditThread("G:\\","balking.txt").start(); } } /** * Balking模式在日常的开发中很常见,比如在系统资源的加载或者某些数据的初始化时, * 在整个系统中声明中期中资源可能只被加载一次,我们就可以采用balking模式加以解决, * 代码如下: * public synchronized Map<String, Resource> load() * { * //balking * if(load) * { * return resourceMap; * }else * { * //do the resource load task; * //.... * this.loaded = true; * return resoureMap; * } * } * */