設計模式之-兩階段終止模式(Two-Phase Termination Patter)模式


        首先兩階段終止模式不是23種傳統設計模式中的,它是由黃文海在《Java多線程編程實戰指南 設計模式》中所提到的模式,現一共可歸納爲36種設計模式
  • 當我們想要結束一個線程或者關閉jvm的時候,通過此模式可以優雅安全的關閉線程,讓線程可以完成它本應完成的當前任務並可以附加一些收尾工作後再進行關閉
  • 此模式下關閉線程會有一定延遲,主要在於被關閉線程需要執行完後,再進行關閉
  • 首先Java jdk中並沒有關於安全的直接的停止線程的Api 我知道你想到了Interrupt
             Interrupt快速解讀傳送門 http://blog.csdn.net/crazyzxljing0621/article/details/56666418

   此模式的設計其實沒什麼精髓之處,其實就是線程中不斷對一個參數進行驗證,滿足條件時線程跳出並執行結束後的收尾工作,最後安全關閉。
         
        在表現此模式以前我們先把volatile再複習一下 傳送門http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
    
        volatile的簡單例子
public class s1 {

	public volatile static int v1 = 0;
	public static int v2 = 0;
	public static int v3 = 0;
	public volatile static int v4 = 0;
	public static AtomicInteger v5 = new AtomicInteger(0);

	public static void inc() {
 
		try {
			Thread.sleep(3);
		} catch (InterruptedException e) {
		}

		v1++;
		v2++;
		v5.getAndIncrement();
		synchronized (s1.class) {
			v3++;
			v4++;
		}
	}

	public static void main(String[] args) throws InterruptedException {
		Thread threads[]=new Thread[1000];
		for (int i = 0; i < 1000; i++) {
			threads[i]=new Thread(new Runnable() {
				@Override
				public void run() {
					inc();
				}
			});
			threads[i].start();
		} 
		for(Thread t:threads)
			t.join();
		System.out.println("運行結果v1:Counter.count=" + s1.v1);
		System.out.println("運行結果v2:Counter.count=" + s1.v2);
		System.out.println("運行結果v3:Counter.count=" + s1.v3);
		System.out.println("運行結果v4:Counter.count=" + s1.v4);
		System.out.println("運行結果v5:Counter.count=" + s1.v5);
	}
}
運行結果v1:Counter.count=994
運行結果v2:Counter.count=998
運行結果v3:Counter.count=1000
運行結果v4:Counter.count=1000
運行結果v5:Counter.count=1000 
         
好了我們開始展現這個【兩階段終止模式】
先來看最簡單的實現方式
/**
 * 
 * @author Allen
 * @date 2017年2月21日
 *
 */
public class e1 implements Runnable {
	
	private volatile boolean closeThread = false;
	private int i;

	// 終止請求
	public void shutdownRequest() {
		closeThread = true;
		Thread.currentThread().interrupt();
	}

	public void close() {
		this.closeThread = true;
	}

	public boolean isCloseThread() {
		return closeThread;
	}

	// 具體動作
	public final void run() {
		try {
			while (!closeThread) {
				System.out.println("維修進度" + ++i + "%");
				Thread.sleep(1);
			} 
		} catch (InterruptedException e) {
		} finally {
			try {
				overFun();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	} 
	// 終止處理
	private void overFun() throws InterruptedException {
		Thread.sleep(1000);
		System.out.println(">>>>>正在收拾工具");
		Thread.sleep(1000);
		System.out.println(">>>>>正在把瑪莎拉蒂開出車庫");
		Thread.sleep(1000); 
		System.out.println(">>>>>正在用瑪莎拉蒂車載電話呼叫領導的手機");
		Thread.sleep(1000);
		System.out.println(">>>>>我:領導你好,土豪我今天的總維修進度 " + i + "%");
	}
}

首先我們的標示使用volatile修飾,我們僅僅用它做了一個布爾型的判定用參數,滿足了其可見性,又不對他進行頻繁修改而去觸摸那原子性問題
上面代碼我們每次都判斷closeThread是否爲真,如果if滿足則進入finally並執行overFun以安全結束線程及其業務,保證了我們數據、業務的安全性

public static void main(String[] args) {
		System.out.println("開始修電腦");
		try {
			e1 e = new e1();
			Thread t = new Thread(e);
			t.start();
			Thread.sleep(new Random().nextInt(30)+69);
			System.out.println("大喇叭廣播: ~都快別修了");
			e.close(); 
			t.join();
			System.out.println("領導:..你居然開瑪莎拉蒂");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

打印結果
開始修電腦
維修進度1%
維修進度2%
維修進度3% 
...
...
...
維修進度40%
維修進度41%
維修進度42%
維修進度43%
大喇叭廣播: ~都快別修了
>>>>>正在收拾工具
>>>>>正在把瑪莎拉蒂開出車庫
>>>>>正在用瑪莎拉蒂車載電話呼叫領導的手機
>>>>>我:領導你好,開着瑪莎拉蒂的我今天總維修進度 43%
領導:..你居然開瑪莎拉蒂

下面看這個例子,其實就是做的複雜一些的兩階段中止模式,啓動服務器,運行所有活動,關閉活動的時候每個活動都會調用overEvent



package TwoPhaseTerminationPattern.complex.sys.inf;

import TwoPhaseTerminationPattern.complex.ThreadOverException;
/**
 * 遊戲系統
 * @author Administrator
 *
 */
public abstract class IGameSys {
	private long time;
	private String name;

	public IGameSys(long time, String name) {
		this.time = time;
		this.name = name;
	}
 

	public String name() {
		return this.name;
	}

	public long time() {
		return this.time;
	}

	public abstract void event(Object... ob) throws ThreadOverException;

	public abstract void overEvent();

}
package TwoPhaseTerminationPattern.complex.sys;

import TwoPhaseTerminationPattern.complex.ThreadOverException;
import TwoPhaseTerminationPattern.complex.sys.inf.IGameSys;

/**
 * 活動系統實現
 * 
 * @author Allen
 * @date 2017年2月21日
 * 
 */
public class ActivitySys extends IGameSys {

	public ActivitySys(long time, String name) {
		super(time, name);
	}

	private boolean temp = false;

	@Override
	public void event(Object... ob) throws ThreadOverException {
		if (!temp){
			System.out.println("<Thread-" + super.name() + ">[event]活動系統運行中 ><");
			temp=true; 
		}
	}

	@Override
	public void overEvent() {
		System.out.println("<Thread" + super.name() + ">[overEvent]");
	}

}
package TwoPhaseTerminationPattern.complex.sys;

import TwoPhaseTerminationPattern.complex.ThreadOverException;
import TwoPhaseTerminationPattern.complex.sys.inf.IGameSys;

/**
 * 公告系統實現
 * 
 * @author Allen
 * @date 2017年2月21日
 * 
 */
public class MsgSys extends IGameSys {

	public MsgSys(long time, String name) {
		super(time, name);
	}
	private boolean temp=false;
	@Override
	public void event(Object... ob) throws ThreadOverException {
		if (!temp){
		System.out.println("<Thread-" + super.name() + ">[event]系統公告運行中 ><");
		temp=true;
		}
	}

	@Override
	public void overEvent() {
		System.out.println("<Thread" + super.name() + ">[overEvent]");
	}

}
package TwoPhaseTerminationPattern.complex;

public interface IRepairModel extends Runnable {

}
package TwoPhaseTerminationPattern.complex;

import TwoPhaseTerminationPattern.complex.sys.inf.IGameSys;

public class RepairModelImpl implements IRepairModel {

	private IGameSys ir;

	public RepairModelImpl(IGameSys ir) {
		this.ir = ir;
	}

	@Override
	public void run() {
		System.out.println(ir.name() + "已啓動");
		try {
			while (true) {
				Thread.sleep(ir.time());
				if (SystemState.getInstance().isState())
					break;
				ir.event();
			}
		} catch (ThreadOverException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			ir.overEvent();
		}
		return;
	}
}

package TwoPhaseTerminationPattern.complex;

import java.util.Arrays;
import java.util.Iterator;

import TwoPhaseTerminationPattern.complex.sys.ActivitySys;
import TwoPhaseTerminationPattern.complex.sys.MsgSys;
import TwoPhaseTerminationPattern.complex.sys.inf.IGameSys;
/**
 * 
 * @author Allen 
 * @date 2017年2月22日
 *
 */
public class run {
	static IGameSys[] igame = { 
		new MsgSys(500,"公告系統"), 
		new ActivitySys(200,"活動系統")
		}; 
	public static void main(String[] args) throws InterruptedException {
		System.out.println("遊戲服務器已啓動");
		startThreadGroup();
		Thread.sleep(4000);
		SystemState.getInstance().sendState();

	}
	private static void startThreadGroup() throws InterruptedException { 
		Iterator<IGameSys> it = Arrays.asList(igame).iterator();
			while (it.hasNext()) {
				Thread thread = new Thread(new RepairModelImpl(it.next()));
				thread.start(); 
			} 
	}
}
package TwoPhaseTerminationPattern.complex;

/**
 * 修復狀態
 * 
 * @author Allen
 * @date 2017年2月21日
 *
 */
public final class SystemState {
	/**
	 * 餓漢單例
	 */
	private static SystemState instance = new SystemState();

	private SystemState() {
	}

	public static SystemState getInstance() {
		return instance;
	}

	private volatile boolean state = false;

	public boolean isState() {
		return state;
	}

	/**
	 * 發送狀態 
	 * @author Allen
	 * @date 2017年2月21日
	 */
	public void sendState() {
		this.state = true;

	}

}
package TwoPhaseTerminationPattern.complex;


public class ThreadOverException extends Exception {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

}
輸出結果:
遊戲服務器已啓動
公告系統已啓動
活動系統已啓動
<Thread-活動系統>[event]活動系統運行中 ><
<Thread-公告系統>[event]系統公告運行中 ><
<Thread活動系統>[overEvent]
<Thread公告系統>[overEvent]

IGameSys  系統服務模型
  定義了系統執行週期,系統名,規範了event和overevent,IgameSys及其派生類無需關心Thread細節,只需完成event/overevent業務操作即可
IRepairModel 空接口繼承了runnable
  日後擴展,內容規範,依賴規範
RepairModelImpl 線程細節實現
  對IgameSys進行了兩階段終止模式實現
SystemState 修復狀態容器
  單例且volatile,sendState開閉

就到這裏了。代碼還有很多可以優化的地方,不過畢竟是個例子,目的爲了瞭解兩階段終止模式的使用 

   
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章