1 什么是Balking设计模式
多个线程监控某个共享变量,A线程监控到共享变量发生变化后即将触发某个动作,但是此时发现有另外一个线程B已经针对该变量的变化开始了行动,因此A便放弃了准备开始的工作,我们把这样的线程间交互称为Balking(犹豫)设计模式。
其实这样的场景在生活中很常见,比如你去饭店吃饭,吃到途中想要再点一个小菜,于是你举起手示意服务员,其中一个服务员看到了你举手正准备走过来的时候,发现距离你比较近的服务员已经准备要受理你的请求于是中途放弃了。
我们在用word编写文档的时候,每次的文字编辑都代表着文档的状态发生了改变,除了我们可以使用ctrl+s快捷键手动保存以外,word软件本身也会定期触发自动保存,如果word自动保存文档的线程在准备执行保存动作的时候,恰巧我们进行了主动保存,那么自动保存文档的线程将会放弃此次的保存动作。
简短截说就是某个线程因为发现其他线程正在进行相同的工作而放弃即将开始的任务
2 Balking模式之文档编辑
2.1 代表文档的Document类
public class Document {
/**
* 代表文档是否发生变化,发生改变则设置为true
*/
private boolean changed = false;
/**
* 一次需要保存的内容,可以理解为内容缓存
*/
private List<String> content = new ArrayList<>();
private final FileWriter fileWriter;
/**
* 用于自动保存的线程
*/
private static AutoSaveThread autoSaveThread;
/**
* /构造函数需要传入文档保存的路径和文档名称
*/
public Document(String documentPath, String documentName) throws IOException {
this.fileWriter = 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提交字符串
*
* @param content
*/
public void edit(String content) {
synchronized (this) {
this.content.add(content);
// 修改标志
this.changed = true;
}
}
/**
* 关闭文档
*/
public void close() throws IOException {
// 中止这个自动保存的线程
autoSaveThread.interrupt();
// 关闭流
this.fileWriter.close();
}
/**
* 保存文档
*/
public void save() throws IOException {
synchronized (this) {
if (!changed) {
// 文档已经被保存了,则直接返回
return;
}
System.out.println(Thread.currentThread().getName() + " execute save action");
// 将内容写入文档
for (String changeLine : this.content) {
this.fileWriter.write(changeLine);
this.fileWriter.write("\r\n");
}
this.fileWriter.flush();
// 修改标志
this.changed = false;
// 清除content
this.content.clear();
}
}
}
- edit方法和save方法进行方法同步,其目的在于防止当文档在保存的过程中如果遇到新的内容被编辑时引起的共享资源冲突问题。
- changed在默认情况下为false,当有新的内容被编辑的时候将被修改为true。
- 在进行文档保存的时候,首先查看changed是否为true,如果文档发生过编辑则在文档中保存新的内容,否则就会放弃此次保存动作,changed是balking pattern关注的状态,当changed为true的时候就像远处的服务员看到客户的请求被另外一个服务员接管了一样,于是放弃了任务的执行。
- 在创建Document的时候,顺便还会启动自动保存文档的线程,该线程的主要目的在于在固定时间里执行一次文档保存动作。
2.2 自动保存文档线程
与平日里编写word文档一样,word会定期自动保存我们编辑的文档,如果在电脑出现故障重启之时,没有来得及对文档保存,也不至于损失太多劳动成果,它甚至能够百分之百的恢复,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 {
// 每隔1s自动保存
TimeUnit.SECONDS.sleep(1);
document.save();
} catch (InterruptedException e) {
// 收到了中断信号,就跳出循环,中止的这个线程
break;
}catch (IOException e){
e.printStackTrace();
}
}
}
}
2.3 测试
为了模拟,定义一个线程用于编辑文档
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)) {
// 用户输入的是quit,就表示退出
// 保存文件
document.save();
break;
}
// 将内容编辑到文档中
document.edit(text);
if (times == 5) {
// 用户输入了5次之后,执行一次保存
document.save();
// 重置计数
times = 0;
}
times++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
DocumentEditThread documentEditThread = new DocumentEditThread("D:\\", "h.txt ");
documentEditThread.start();
}
3 总结
Balking模式在日常的开发中很常见,比如在系统资源的加载或者某些数据的初始化时,在整个系统中声明周期中资源可能只被加载一次,我们就可以采用balking模式加以解决