暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Two-Phase-Termination设计模式(上)

Alleria Windrunner 2020-06-03
258

什么是 Two Phase Termination 模式

当一个线程正常结束,或者因被打断而结束,或者因出现异常而结束时,我们需要考虑如何同时释放线程中资源,比如文件句柄、socket 套接字句柄、数据库连接等比较稀缺的资源。Two Phase Termination 设计模式如图所示。

如上图所示,我们使用“作业中”表示线程的执行状态,当希望结束这个线程时,发出线程结束请求,接下来线程不会立即结束,而是会执行相应的资源释放动作直到真正的结束,在终止处理状态时,线程虽然还在运行,但是进行的是终止处理工作,因此终止处理又称为线程结束的第二个阶段,而受理终止要求则被称为线程结束的第一个阶段。
在进行线程两阶段终结的时候需要考虑如下几个问题。
  • 第二阶段的终止保证安全性,比如涉及对共享资源的操作。

  • 要百分之百地确保线程结束,假设在第二个阶段出现了死循环、阻塞等异常导致无法结束。

  • 对资源的释放时间要控制在一个可控的范围之内。



Two Phase Termination 的示例

线程停止的 Two Phase Termination

Two Phase Termination 与其说是一个模式,还不如说是线程使用的一个技巧(best practice)。其主要针对的是当线程结束生命周期时,能有机会做一些资源释放的动作,我们在之前的文章中定义了用于处理客户端连接的 Handler(ClientHandler),在本篇中我们继续使用它作为我们的示例:
    @Override
    public void run()
    {
    try
    {
    this.chat();
    } catch (IOException e)
    {
    e.printStackTrace();
    }
    }
    private void chat() throws IOException
    {
    BufferedReader bufferedReader = wrap2Reader(this.socket.getInputStream());
    PrintStream printStream = wrap2Print(this.socket.getOutputStream());
    String received;
    while ((received = bufferedReader.readLine()) != null)
    {
    System.out.printf("client:%s-message:%s\n", clientIdentify, received);
    if (received.equals("quit"))
    {
    write2Client(printStream, "client will close");
    socket.close();
    break;
    }
    write2Client(printStream, "Server:" + received);
    }
    }
    当客户端发送 quit 指令时,服务端会断开与客户端的连接“socket.close();”,如果客户端发送正常信息后发生异常,chat 方法会抛出错误,那么此时该线程的任务执行也将结束,socket 和 thread 一样都属于严重依赖操作系统资源的对象,各个操作系统中可供创建的线程数量有限,为了能够确保客户端即使异常关闭,我们也能够尽快地释放 socket 资源,two phase Termination 设计模式将是一种比较好的解决方案,示例代码如下:
      @Override
      public void run()
      {
      try
      {
      this.chat();
      } catch (IOException e)
      {
      e.printStackTrace();
      } finally
      {
      //任务执行结束时,执行释放资源的工作
      this.release();
      }
      }
      private void release()
      {
      try
      {
      if (socket != null)
      {
      socket.close();
      }
      } catch (Throwable e)
      {
      //ignore
      }
      }
      当 chat 方法出现异常会被 run 方法捕获,在 run 方法中加入 finally 子句,用于执行客户端 socket 的主动关闭,为了使客户端的关闭不影响线程任务的结束,我们捕获了 Throwable 异常(在关闭客户端连接时出现的异常,视为不可恢复的异常,基本上没有针对该异常进行处理的必要)。
      上面的这个例子是针对线程(任务)关闭时进行资源释放的举例,那么当一个进程主动关闭或者异常关闭的时候,可不可以也进行两阶段的 termination 呢?


      进程关闭的 Two Phase Termination

      如同线程关闭需要进行资源释放一样,不论是进程的主动关闭还是被动关闭(出现异常)都需要对持有的资源进行释放,通过为进程注入一个或者多个 hook 线程的方式进行两阶段的结束。
      文章转载自Alleria Windrunner,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

      评论