导语
在实际操作中当某个线程因为发现其他线程正在进行相同的工作而放弃即将开始的任务,这种情况就被称为是Balking模式,Balking英文的意思是犹豫。在多个线程监控某个共享变量,A线程监控到共享变量发生变化后立即触发某个动作,但是这个这个时候发现了B线程也对该变量开始了行动,这个时候A变量就放弃了准备工作。下面就来详细的讲解一下关于Balking模式
什么是Balking模式
在餐厅吃饭的时候,当你吃到一半的时候,你还想加点小菜,于是你叫来服务于开始点菜,这个时候,他会发现离你较近的一个服务员已经开始走进你的时候,他就不会在走进你了。这个时候该服务员就会放弃对你的服务。
在编写Word文档的时候,每次的文字编辑都代表这文档的状态会发生变化,除了使用快捷键进行保存之外,Word本身也在触发一个保存机制,这个时候如果Word正要执行自动保存,这个时候你使用了快捷键进行保存,那么自动保存就不会执行了。
通过上面这个例子形象的了解了Balking设计模式,下面就来通过一个小例子来深入的了解一下Balking模式。
Balking模式文档编辑器
Document 类设计
在正如上面提到的Document类主要有两个功能edit 和save功能。下面就来看看详细代码
public class Document {
//如果文档发生改变,change 会被设置为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 IOException {
Document document = new Document(documentPath,documentName);
autoSaveThread = new AutoSaveThread(document);
autoSaveThread.start();
return document;
}
public void edit(String content){
synchronized (this){
this.content.add(content);
this.changed = true;
}
}
public void close() throws IOException {
autoSaveThread.interrupt();
writer.close();
}
public void save() throws IOException {
synchronized(this){
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也就是标识了一个是否被编辑的标识位
- 3、在保存操作开始之前首先查看标识,是否处于被修改状态,如果是则放弃保存操作,如果不是则进行保存操作,也就是说changed是Balking模式比较关注的一个方面。有点类似于当发现有人进行操作时就会放弃本次操作。
- 4、在创建这个文档的时候,这个自动保存的操作线程就已经被创建了,并且这个线程会在规定的时间内执行保存操作。
自动保存线程类
在平时使用IDEA的时候,它并不是在支持一个很好的代码编写功能,而且在指定的操作之后还会自动的保存编写的代码,这样在很多的场景下就可以实现不用自己保存的操作。类似的操作在很多的地方都会用到。这个是因为它后台有一个自动保存文件的服务一直处于开启状态。在上面的代码中也提到了这个操作。下面是这个操作的代码内容。从代码中来看就是在指定的时间内进行一次代码的保存操作。
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;
}
}
}
}
编辑线程类
结合上面的内容有了自动保存的功能,就要有编辑的功能。下面就来看看编辑功能的类
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 time = 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 (time == 5){
document.save();
time = 0;
}
time++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这个类中主要操作就是一直监听用户的输入操作,并且将输入的操作写入到文件中。编写测试类
编写测试类
public class BalkingTest {
public static void main(String[] args) {
new DocumentEditThread("./","balking.txt").start();
}
}
测试结果
从代码测试结果来看,每次执行五次操作之后会有一次保存操作。这个类似于主动的点击了保存按钮。
总结
在日常的开发模式中,在资源的加载或者是资源初始化的过程中,有些资源在整个的生命周期中只加载异常,可以使用Balking模式进行加载。
public synchronized Map<String,Resource> load(){
if(loaded){
return resouceMap;
}else{
//do something to load resouce
//
this.loaded = true;
return resouceMap;
}
}
在这个方面类似于Spring 源码中的加载形式,只不过在Spring源码中的加载是通过统一的load方法来加载,不会出现线程安全问题。上面这种方式类似于使用了单例模式。但是它与单例模式还是有一定的差距。所以说还需要后续在实际使用中多用到才能真正体会到两种模式的差异。