1. 程式人生 > >多線程系列之五:Balking 模式

多線程系列之五:Balking 模式

實現 vat 正常 bject ret .get 完全 .data 工具

一,什麽是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 模式