多线程系列之五:Balking 模式
Posted inspred
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程系列之五:Balking 模式相关的知识,希望对你有一定的参考价值。
一,什么是Balking模式
如果现在不合适执行这个操作,或者没必要执行这个操作,就停止处理,直接返回。
在Balking模式中,如果守护条件不成立,就立即中断处理。
二,例子:
定期将当前数据内容写入文件中,比如文本工具的自动保存功能,定期的将数据保存到文件中。
当数据内容被写入时,会完全覆盖上次写入的内容,只有最新的内容才会被保存
当写入的内容和上次的内容完全相同时,再向文件写入就多余了,所以就不再执行写入操作。
所以这个程序就是以 数据内容不同 作为守护条件,如果数据内容相同,就不执行写入操作,直接返回(balk)
代码:
Data类:可以修改并保存的数据的类
ChangerThread类:模仿用户,进行文本的修改并随时保存
SaverThread类:执行自动保存的线程
public class Data { private final String filename; private String content; private boolean changed; public Data(String filename, String content) { this.filename = filename; this.content = content; this.changed = true; } //修改了数据内容 public synchronized void change(String newContent){ content = newContent; changed = true; } //若数据修改过,则保存到文件中 public synchronized void save() throws IOException{ if (!changed){ //如果没有修改,就不保存了 return; } doSave(); changed = false; } //将数据内容保存到文件中 public void doSave() throws IOException{ System.out.println(Thread.currentThread().getName()+" calls doSave, content ="+content); Writer writer = new FileWriter(filename); writer.write(content); writer.close(); } }
public class ChangerThread extends Thread{ private final Data data; private final Random random = new Random(); public ChangerThread(String name,Data data) { super(name); this.data = data; } @Override public void run() { try { for (int i = 0; true; i++) { data.change("NO."+i);//修改数据 Thread.sleep(random.nextInt(1000));//执行其他操作 data.save();//显示的保存,用户自己点击保存 } }catch (IOException e){ e.printStackTrace(); }catch (InterruptedException e){ e.printStackTrace(); } } }
public class SaverThread extends Thread{ private final Data data; public SaverThread(String name,Data data) { super(name); this.data = data; } @Override public void run() { try { while (true){ data.save();//要求保存数据 Thread.sleep(1000);//休眠约一秒 } }catch (IOException e){ e.printStackTrace(); }catch (InterruptedException e){ e.printStackTrace(); } } }
public class Test { public static void main(String[] args) { Data data = new Data("data.txt","(empty)"); new ChangerThread("ChangeThread",data).start(); new SaverThread("SaverThread",data).start(); } }
三,GurardedObject:被防护的对象
GuardedObject角色是一个拥有被防护的方法的类。当线程执行guardedMethod时,若守护条件成立,则执行实际的处理。
当守护条件不成立时,不执行实际的处理,直接返回。守护条件是否成立,会随着GuardedObject角色的状态变化而变化
Data类就是被防护的对象
save方法对应guardedMethod
change方法对应stateChangingMethod
四,何时使用Balking模式
1.并不需要执行时:
比如上面的程序中,文件已经手动保存了,并且文本内容也没有改变,就不需要再执行自动保存了。这样可以提高程序性能
2.不需要等待守护条件成立时
当守护条件不成立时,就立即返回并进入下一个操作,就可以使用这种模式。这能够提高程序的响应性
3.守护条件仅在一次成立时
相当于没有stateChangingMethod方法的情况
例子:
public class Something{ private boolean initialized = false; public synchronized void init(){ if(initialized){ return; } doInit(); initialized = true; } public void doInit(){ //实际处理初始化 } }
//如果没有初始化,就做一次初始化动作。如果已经初始化就什么也不做。直接返回
五,balk结果的表示方式
1.忽略balk
不通知调用端发生了balk,比如实例程序中直接return
2.通过返回值来表示balk
比如返回值为true,表明为发生balk。如果返回值为false,说明发生了balk,处理并未被执行
3.通过异常来表示balk的发生
当balk发生时,程序并不是从方法中retrun,而是直接抛出异常
六,超时
Balking模式中,当守护条件不成立时,线程会直接balk并返回
而Guarded Suspension模式中,当守护条件不成立时,线程会一直等待到成立为止。
这两种模式都比较极端。还有一种处理方方法,在守护条件成立之前等待一段时间,如果到时条件还未成立,则直接balk。这种处理称为 guarded timed或timeout
1.wait何时终止?
当执行: obj.wait();时,线程进入obj的等待队列,停止运行,并释放持有的obj锁。当下面情况发生时,线程就会退出等待线程:
notify方法执行
notifyAll方法执行
interrupt方法执行
超时发生时
2.guarded timed的实现
从上面的方法得知,我们无法区分wait方法是被notify/notifyAll了,还是超时了,所以自己要实现guarded timed。判断wait是否超时
代码:
public class Host { private final long timeout;//超时时间 private boolean ready = false;//方法正常执行时为true public Host(long timeout){ this.timeout = timeout; } //修改状态 public synchronized void setExecutable(boolean on){ ready =on; notifyAll(); } //检查状态后在执行 public synchronized void execute()throws InterruptedException,TimeoutException{ long start = System.currentTimeMillis();//开始时间 while (!ready){ long now = System.currentTimeMillis();//当前时间 long rest = timeout - (now -start);
//在这里只要 <=0,我们就认为超时。(因为wait方法的参数为0时,就表示没有超时时间(超时时间无限长),当传入负数时,会抛出IllegalArgumentException异常。) if (rest <= 0){ throw new TimeoutException("now - start= "+(now-start)+", timeout = "+timeout); } wait(rest); } doExecute(); } //实际的处理 private void doExecute(){ System.out.println(Thread.currentThread().getName()+" calls doExecute"); } }
/*
如果Host的方法执行时,不调用setExecutable(true),程序就会出现超时
*/
public class Test { public static void main(String[] args) { Host host = new Host(10000); try { System.out.println("execute Begin"); host.execute(); }catch (TimeoutException e){ e.printStackTrace(); }catch (InterruptedException e){ e.printStackTrace(); } } }
执行结果:
execute Begin
java.util.concurrent.TimeoutException: now - start= 10001, timeout = 10000 //误差1毫秒
at com.amazing.jdk.myThread.package12.Host.execute(Host.java:29)
at com.amazing.jdk.myThread.package12.Test.main(Test.java:14)
以上是关于多线程系列之五:Balking 模式的主要内容,如果未能解决你的问题,请参考以下文章
JUC并发编程 多线程设计模式 -- 同步模式之 Balking
Android多线程分析之五:使用AsyncTask异步下载图像