Java多線程19 兩階段終止模式(Two-Phase Termination Patter)

Java多線程目錄

有時候啼辣,我們希望提前結(jié)束線程,但安全可靠地停止線程御滩,并不是一件容易的事情鸥拧,如果立即停止線程,會使共享的數(shù)據(jù)結(jié)構(gòu)處于不一致的狀態(tài)削解,如目前已經(jīng)廢棄使用的Thread類的stop方法(它會使線程在拋出java.lang.ThreadDeath之后終止線程富弦,即使是在執(zhí)行synchronized方法的時候)。更好的做法是執(zhí)行完終止處理氛驮,再終止線程腕柜,即Two-phase Termination,兩階段終止模式矫废。

該模式有兩個角色:

  • Terminator盏缤,終止者,負(fù)責(zé)接收終止請求蓖扑,執(zhí)行終止處理唉铜,處理完成后再終止自己。
  • TerminationRequester:終止請求發(fā)出者律杠,用來向Terminator發(fā)出終止請求潭流。

該模式示例代碼如下:
Terminator:

public class CounterIncrement extends Thread {

    private volatile boolean terminated = false;

    private int counter = 0;

    private Random random = new Random(System.currentTimeMillis());
    @Override
    public void run() {

        try {
            while (!terminated) {
                System.out.println(Thread.currentThread().getName()+" "+counter++);
                Thread.sleep(random.nextInt(1000));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            this.clean();
        }
    }

    private void clean() {
        System.out.println("do some clean work for the second phase,current counter "+counter);

    }

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

TerminationRequester:

public class CounterTest {
    public static void main(String[] args) throws InterruptedException {
        CounterIncrement counterIncrement = new CounterIncrement();
        counterIncrement.start();

        Thread.sleep(15_000L);
        //主動清理
        counterIncrement.close();
    }
}

這段代碼可以看出實現(xiàn)兩階段終止模式必須注意的是:
使用線程停止標(biāo)志和interrupt方法竞惋,兩者缺一不可

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

這里使用了terminated作為線程停止標(biāo)志,變量采用volatile修飾灰嫉,避免了使用顯式鎖的開銷拆宛,又保證了內(nèi)存可見性。線程run方法會檢查terminated屬性熬甫,如果屬性為true胰挑,就停止線程,但線程可能調(diào)用了阻塞方法椿肩,處于wait狀態(tài)瞻颂,任務(wù)也就可能永遠(yuǎn)不會檢查terminated標(biāo)志;線程也有可能處于sleep()狀態(tài)郑象,等sleep時間過后再執(zhí)行終止?fàn)顟B(tài)贡这,程序的響應(yīng)性就下降了。你可以把方法改成如下運(yùn)行厂榛,線程停止明顯變慢了許多:

  public void close() {
        terminated = true;
  }
模擬客戶端或者服務(wù)端都可能終止服務(wù)的例子
public class AppServer extends Thread {

    private static final int DEFAULT_PORT = 12722;
    private final static ExecutorService executor = Executors.newFixedThreadPool(10);
    private int port;
    private volatile boolean start = true;
    private List<ClientHandler> clientHandlers = new ArrayList<>();
    private ServerSocket server;

    public AppServer() {
        this(DEFAULT_PORT);
    }

    public AppServer(int port) {
        this.port = port;
    }

    @Override
    public void run() {
        try {
            server = new ServerSocket(port);
            while (start) {
                Socket client = server.accept();
                ClientHandler clientHandler = new ClientHandler(client);
                executor.submit(clientHandler);
                this.clientHandlers.add(clientHandler);
            }

        } catch (IOException e) {
            //throw new RuntimeException();
        } finally {
            this.dispose();
        }
    }

    public void dispose() {
        System.out.println("dispose");
        this.clientHandlers.stream().forEach(ClientHandler::stop);
        this.executor.shutdown();
    }

    public void shutdown() throws IOException {
        this.start = false;
        this.interrupt();
        this.server.close();
    }
}
public class ClientHandler implements Runnable {

    private final Socket socket;

    private volatile boolean running = true;

    public ClientHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {


        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream();
             BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
             PrintWriter printWriter = new PrintWriter(outputStream)) {
            while (running) {
                String message = br.readLine();
                if (message == null) {
                    break;
                }
                System.out.println("Come from client >" + message);
                printWriter.write("echo " + message+"\n");
                printWriter.flush();
            }
        } catch (IOException e) {
            //自動關(guān)閉的時候 將running
            this.running = false;
        }finally {
            this.stop();
        }

    }

    public void stop() {
        if (!running) {
            return;
        }
        this.running = false;
        try {
            this.socket.close();

        } catch (IOException e) {

        }
    }
}
public class AppServerClient {
    public static void main(String[] args) throws InterruptedException, IOException {
        AppServer server = new AppServer(12135);
        server.start();

        Thread.sleep(20_000L);
        server.shutdown();
    }
}

mac telnet模擬客戶端輸入

bogon:~ kpioneer$ telnet localhost 12135
Trying ::1...
Connected to localhost.
Escape character is '^]'.
hello 
echo hello 
I love you
echo I love you
Connection closed by foreign host.

服務(wù)端輸出:

Come from client >hello 
Come from client >I love you
dispose

總結(jié):

可以看到盖矫,在子類使用兩階段終止模式時,其只需要實現(xiàn)各自所需要執(zhí)行的任務(wù)击奶,并且更新當(dāng)前任務(wù)的數(shù)量即可辈双。在某些情況下,當(dāng)前任務(wù)的數(shù)量也可以不進(jìn)行更新柜砾,比如在進(jìn)行終止時湃望,不關(guān)心當(dāng)前剩余多少任務(wù)需要執(zhí)行。

特別感謝:

多線程設(shè)計模式解讀—Two-phase Termination痰驱,兩階段終止模式(承諾)模式

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末证芭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子担映,更是在濱河造成了極大的恐慌废士,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝇完,死亡現(xiàn)場離奇詭異官硝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)短蜕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門泛源,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事★鄙#” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵缎玫,是天一觀的道長。 經(jīng)常有香客問我解滓,道長赃磨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任洼裤,我火速辦了婚禮邻辉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘腮鞍。我一直安慰自己值骇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布移国。 她就那樣靜靜地躺著吱瘩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪迹缀。 梳的紋絲不亂的頭發(fā)上使碾,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音祝懂,去河邊找鬼票摇。 笑死,一個胖子當(dāng)著我的面吹牛砚蓬,可吹牛的內(nèi)容都是我干的矢门。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼怜械,長吁一口氣:“原來是場噩夢啊……” “哼颅和!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起缕允,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤峡扩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后障本,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體教届,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年驾霜,在試婚紗的時候發(fā)現(xiàn)自己被綠了案训。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡粪糙,死狀恐怖强霎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蓉冈,我是刑警寧澤城舞,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布轩触,位于F島的核電站,受9級特大地震影響家夺,放射性物質(zhì)發(fā)生泄漏脱柱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一拉馋、第九天 我趴在偏房一處隱蔽的房頂上張望榨为。 院中可真熱鬧,春花似錦煌茴、人聲如沸随闺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽板壮。三九已至,卻和暖如春合住,著一層夾襖步出監(jiān)牢的瞬間绰精,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工透葛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留笨使,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓僚害,卻偏偏與公主長得像硫椰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子萨蚕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容