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

 更新时间:2021年10月28日 14:47:22   作者:冬日毛毛雨  
这篇文章主要介绍了Java多线程 两阶段终止模式Two-Phase Termination Patter,该模式有两个角色,分别是Terminator,终止者,负责接收终止请求,执行终止处理,处理完成后再终止自己。TerminationRequester终止请求发出者,用来向Terminator发出终止请求,需要的朋友可以参考一下

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

总结:

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

到此这篇关于Java多线程 两阶段终止模式Two-Phase Termination Patter的文章就介绍到这了,更多相关Java多线程 两阶段终止模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot+Email发送邮件的实现示例

    SpringBoot+Email发送邮件的实现示例

    Spring Boot提供了简单而强大的邮件发送功能,本文主要介绍了SpringBoot+Email发送邮件的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • IDEA创建Java项目文件并运行教程解析

    IDEA创建Java项目文件并运行教程解析

    这篇文章主要介绍了IDEA创建Java项目文件并运行教程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • SpringBoot3文件管理操作方法

    SpringBoot3文件管理操作方法

    这篇文章主要介绍了SpringBoot3文件管理,本文案例只围绕普通文件和Excel两种类型进行代码实现,包括工程搭建、上传下载操作,需要的朋友可以参考下
    2023-08-08
  • Eclipse连接Mysql数据库操作总结

    Eclipse连接Mysql数据库操作总结

    这篇文章主要介绍了Eclipse连接Mysql数据库操作总结的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-08-08
  • Nacos设置为windows自启动服务的步骤详解

    Nacos设置为windows自启动服务的步骤详解

    这篇文章给大家介绍了Nacos设置为windows自启动服务的操作步骤,文中通过代码示例和图文结合讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-12-12
  • Java中字符串与日期转换常见方法总结

    Java中字符串与日期转换常见方法总结

    这篇文章主要给大家介绍了关于Java中字符串与日期转换常见方法的相关资料,在Java编程中经常需要将字符串表示的日期转换为日期对象进行处理,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-11-11
  • IDEA使用学生邮箱无法注册问题:JetBrains Account connection error: 拒绝连接

    IDEA使用学生邮箱无法注册问题:JetBrains Account connection error: 拒绝连接

    这篇文章主要介绍了IDEA使用学生邮箱无法注册问题:JetBrains Account connection error: 拒绝连接,文中通过图文及示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Java异常Exception详细讲解

    Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等
    2022-07-07
  • 详解eclipse项目中的.classpath文件原理

    详解eclipse项目中的.classpath文件原理

    这篇文章介绍了eclipse项目中的.classpath文件的原理,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • 基于Spring p标签和c标签注入方式

    基于Spring p标签和c标签注入方式

    这篇文章主要介绍了Spring p标签和c标签注入方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09

最新评论