多線程系列之五: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 模式