Java多线程 两阶段终止模式Two-Phase Termination Patter

作者:冬日毛毛雨 时间:2023-11-29 04:47:04 

目录
  • 1、两阶段终止模式介绍

  • 2、Terminator代码演示

  • 3、TerminationRequester

  • 4、模拟客户端或者服务端都可能终止服务的例子

  • 5、mac telnet模拟客户端输入

1、两阶段终止模式介绍

有时候,我们希望提前结束线程,但安全可靠地停止线程,并不是一件容易的事情,如果立即停止线程,会使共享的数据结构处于不一致的状态,如目前已经废弃使用的Thread类的stop方法(它会使线程在抛出java.lang.ThreadDeath之后终止线程,即使是在执行synchronized方法的时候)。更好的做法是执行完终止处理,再终止线程,即Two-phase Termination,两阶段终止模式。

该模式有两个角色:

  • Terminator,终止者,负责接收终止请求,执行终止处理,处理完成后再终止自己。

  • TerminationRequester:终止请求发出者,用来向Terminator发出终止请求。

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();
   }
}

这段代码可以看出实现两阶段终止模式必须注意的是:

使用线程停止标志和interrupt方法,两者缺一不可


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

这里使用了terminated作为线程停止标志,变量采用volatile修饰,避免了使用显式锁的开销,又保证了内存可见性。线程run方法会检查terminated属性,如果属性为true,就停止线程,但线程可能调用了阻塞方法,处于wait状态,任务也就可能永远不会检查terminated标志;线程也有可能处于sleep()状态,等sleep时间过后再执行终止状态,程序的响应性就下降了。你可以把方法改成如下运行,线程停止明显变慢了许多:


 public void close() {
       terminated = true;
 }

4、模拟客户端或者服务端都可能终止服务的例子


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) {
           //自动关闭的时候 将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.

服务端输出:

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

总结:

可以看到,在子类使用两阶段终止模式时,其只需要实现各自所需要执行的任务,并且更新当前任务的数量即可。在某些情况下,当前任务的数量也可以不进行更新,比如在进行终止时,不关心当前剩余多少任务需要执行。

来源:https://juejin.cn/post/7023614457683116069

标签:Java多线程,终止模式
0
投稿

猜你喜欢

  • 面试官:详细谈谈Java对象的4种引用方式

    2022-09-19 04:52:20
  • 基于java构造方法Vector遍历元素源码分析

    2023-11-25 05:21:40
  • 解决Android Studio4.1没有Gsonfomat插件,Plugin “GsonFormat” is incompatible的问题

    2021-08-27 22:10:37
  • C#中使用IFormattable实现自定义格式化字符串输出示例

    2023-05-31 23:34:04
  • Android编程绘图操作之弧形绘制方法示例

    2021-10-06 00:33:45
  • SpringBoot整合Mybatis简单实现增删改查

    2023-07-05 15:33:44
  • Android编程开发中的正则匹配操作示例

    2022-12-24 15:24:45
  • C# 调用腾讯即时通信 IM的示例

    2021-10-29 16:31:17
  • 详解lambda表达式foreach性能分析

    2023-10-28 17:55:21
  • c# 获取CookieContainer的所有cookies函数代码

    2023-06-17 23:11:30
  • Spring Boot 单元测试JUnit的实践

    2023-08-29 02:02:15
  • Android MotionEvent中getX()和getRawX()的区别实例详解

    2023-01-24 22:06:42
  • Java使用线程同步解决线程安全问题详解

    2022-02-28 02:03:24
  • Java多线程编程中ThreadLocal类的用法及深入

    2022-03-17 03:21:29
  • 异常try catch的常见四类方式(案例代码)

    2023-11-10 23:18:00
  • Idea导入eureka源码实现过程解析

    2023-06-11 11:52:24
  • 带你一文深入认识Java String类

    2023-10-15 10:55:54
  • C#对Word文档的创建、插入表格、设置样式等操作实例

    2021-10-24 23:13:46
  • Java微信支付-微信红包

    2023-11-01 01:07:05
  • SpringBoot Knife4j在线API文档框架基本使用

    2022-03-10 21:27:48
  • asp之家 软件编程 m.aspxhome.com