Java多線程 兩階段終止模式Two-Phase Termination Patter
1、兩階段終止模式介紹
有時(shí)候,我們希望提前結(jié)束線程,但安全可靠地停止線程,并不是一件容易的事情,如果立即停止線程,會使共享的數(shù)據(jù)結(jié)構(gòu)處于不一致的狀態(tài),如目前已經(jīng)廢棄使用的Thread類的stop方法(它會使線程在拋出java.lang.ThreadDeath
之后終止線程,即使是在執(zhí)行synchronized
方法的時(shí)候)。更好的做法是執(zhí)行完終止處理,再終止線程,即Two-phase Termination
,兩階段終止模式。
該模式有兩個(gè)角色:
Terminator
,終止者,負(fù)責(zé)接收終止請求,執(zhí)行終止處理,處理完成后再終止自己。TerminationRequester
:終止請求發(fā)出者,用來向Terminator
發(fā)出終止請求。
2、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(); } }
3、TerminationRequester
public class CounterTest { public static void main(String[] args) throws InterruptedException { CounterIncrement counterIncrement = new CounterIncrement(); counterIncrement.start(); Thread.sleep(15_000L); //主動清理 counterIncrement.close(); } }
這段代碼可以看出實(shí)現(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
時(shí)間過后再執(zhí)行終止?fàn)顟B(tài),程序的響應(yīng)性就下降了。你可以把方法改成如下運(yùn)行,線程停止明顯變慢了許多:
public void close() { terminated = true; }
4、模擬客戶端或者服務(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)閉的時(shí)候 將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(); } }
5、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é):
可以看到,在子類使用兩階段終止模式時(shí),其只需要實(shí)現(xiàn)各自所需要執(zhí)行的任務(wù),并且更新當(dāng)前任務(wù)的數(shù)量即可。在某些情況下,當(dāng)前任務(wù)的數(shù)量也可以不進(jìn)行更新,比如在進(jìn)行終止時(shí),不關(guān)心當(dāng)前剩余多少任務(wù)需要執(zhí)行。
到此這篇關(guān)于Java多線程 兩階段終止模式Two-Phase Termination Patter
的文章就介紹到這了,更多相關(guān)Java多線程 兩階段終止模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解SpringBoot的jar為什么可以直接運(yùn)行
SpringBoot提供了一個(gè)插件spring-boot-maven-plugin用于把程序打包成一個(gè)可執(zhí)行的jar包,本文給大家介紹了為什么SpringBoot的jar可以直接運(yùn)行,文中有相關(guān)的代碼示例供大家參考,感興趣的朋友可以參考下2024-02-02Java大數(shù)運(yùn)算BigInteger與進(jìn)制轉(zhuǎn)換詳解
這篇文章主要介紹了Java大數(shù)運(yùn)算BigInteger與進(jìn)制轉(zhuǎn)換詳解,Java 提供了 BigInteger(大整數(shù))類和 BigDecimal(大浮點(diǎn)數(shù))類用于大數(shù)運(yùn)算,這兩個(gè)類都繼承自 Number 類(抽象類),由于 BigInteger 在大數(shù)運(yùn)算中更常見,需要的朋友可以參考下2023-09-09java中構(gòu)造方法及this關(guān)鍵字的用法實(shí)例詳解(超詳細(xì))
大家都知道,java作為一門內(nèi)容豐富的編程語言,其中涉及的范圍是十分廣闊的,下面這篇文章主要給大家介紹了關(guān)于java中構(gòu)造方法及this關(guān)鍵字用法的相關(guān)資料,需要的朋友可以參考下2022-04-04IDEA報(bào)錯(cuò):無效的源發(fā)行版解決方案
很多小伙伴在刷新maven的時(shí)候總會報(bào) Error:java:無效的源發(fā)行版,下面這篇文章主要給大家介紹了關(guān)于IDEA報(bào)錯(cuò):無效的源發(fā)行版的解決方案,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09