多个线程监控某个共享变量,A 线程监控到共享变量发生变化后即将触发某个动作,但 是此时发现有另外一个线程 B 已经针对该变量的变化开始了行动,因此 A 便放弃了准备 开始的工作,我们把这样的线程间交互称为 Balking(犹豫)设计模式。其实这样的场景 在生活中很常见,比如你去饭店吃饭,吃到途中想要再点一个小菜,于是你举起手示意服务 员,其中一个服务员看到了你举手正准备走过来的时候,发现距离你比较近的服务员已经准 备要受理你的请求于是中途放弃了。
我们举个实例:我们在用 word 编写文档的时候,每次的文字编辑都代表着文档的状态发生了 改变,除了我们可以使用 ctrl+s 快捷键手动保存以外,word 软件本身也会定期触发自动 保存,如果 word 自动保存文档的线程在准备执行保存动作的时候,恰巧我们进行了主动保 存,那么自动保存文档的线程将会放弃此次的保存动作。
接下来代码实现这个案例
这里我们分为三个类分别是:自动保存线程类(AutoSaveThread)、操作文档类(Document)、手动使用文档线程类(DocumentEditThread),在创建一个document对象时会启动AutoSaveThread线程帮助自动保存文档,这里我们就需要设置一个标记来记录文档是否已经改变,在save()方法当中主要依靠这个标记来决定当前线程是否要保存文档。自动保存设置为每5s保存一次。当我们编辑文档直接手动保存,来看自动保存是否执行。
自动保存线程类(AutoSaveThread)
public class AutoSaveThread extends Thread {
private Document document;
public AutoSaveThread(Document document){
super("AutoSaveThread");
this.document = document;
}
@Override
public void run() {
while (true){
//每隔5s 自动保存一次
try {
this.document.save();
TimeUnit.SECONDS.sleep(5);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
}
操作文档类(Document)
public class Document {
//标记文档是否发生改变
private boolean change = false;
//写对象
private final FileWriter fileWriter;
//内容缓存区
private List<String> contents = new ArrayList<>();
//自动保存线程对象
private static AutoSaveThread autoSaveThread;
//构造函数 需要传入文档的保存路径和文档名称
public Document(String documentPath,String documentName) throws IOException {
this.fileWriter = new FileWriter(new File(documentPath,documentName));
}
//创建Document 对象方法 顺便启动自动保存对象
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 synchronized void edit(String content){
this.contents.add(content);
//文档编辑完,文档发生改变
this.change = true;
}
//文档保存 将缓存区的的数据全部写入到文档中
public synchronized void save() throws IOException {
//判断文档是否修改 (没有修改并不需要保存)
if (!change)
return;
for (String cache:contents){
this.fileWriter.write(cache);
this.fileWriter.write("\r\n");
}
this.fileWriter.flush();
System.out.println(Thread.currentThread().getName()+"保存成功,保存的内容为"+this.contents);
this.contents.clear();
//修改标记
this.change = false;
}
//关闭文档方法 中断自动保存线程,关闭输出流
public void close() throws IOException {
autoSaveThread.interrupt();
fileWriter.close();
}
}
手动使用文档线程类(DocumentEditThread)
public class DocumentEditThread extends Thread {
private final Scanner scanner ;
private String documentPath;
private String documentName;
public DocumentEditThread(String documentPath,String documentName ){
super("DocumentEditThread");
this.scanner = new Scanner(System.in);
this.documentPath = documentPath;
this.documentName = documentName;
}
@Override
public void run() {
try {
//创建文档
Document document = Document.create(documentPath, documentName);
while (true){
//获取用户键盘输入
String text = this.scanner.next();
//当输入 /exit 退出文档
if("/exit".equals(text)){
document.close();
break;
}else if ("/save".equals(text)){ //输入 /save 手动保存
document.save();
}else {
//编辑文档
document.edit(text);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:当手动保存后自动保存不会执行
a
AutoSaveThread保存成功,保存的内容为[a]
b
v
f
AutoSaveThread保存成功,保存的内容为[b, v, f]
q
AutoSaveThread保存成功,保存的内容为[q]
w
e
r
tt
y
uAutoSaveThread保存成功,保存的内容为[w, e, r, tt, y]
a
AutoSaveThread保存成功,保存的内容为[a]
a
/save
DocumentEditThread保存成功,保存的内容为[a]
/exit