多线程设计模式:Two Phase Termination设计模式

1 什么是Two Phase Termination模式

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

当希望结束这个线程时,发出线程结束请求,接下来线程不会立即结束,而是会执行相应的资源释放动作直到真正的结束,在终止处理状态时,线程虽然还在运行,但是进行的是终止处理工作,因此终止处理又称为线程结束的第二个阶段,而受理终止要求则被称为线程结束的第一个阶段

在进行线程两阶段终结的时候需要考虑如下几个问题:

  • 第二阶段的终止保证安全性,比如涉及对共享资源的操作。
  • 要百分之百地确保线程结束,假设在第二个阶段出现了死循环、阻塞等异常导致无法结束。
  • 对资源的释放时间要控制在一个可控的范围之内。

Two Phase Termination与其说是一个模式,还不如说是线程使用的一个技巧。其主要针对的是当线程结束生命周期时,能有机会做一些资源释放的动作

2 Two Phase Termination设计模式示例

之前的一个服务端处理代码:

public class ClientHandler implements Runnable {

    private final Socket socket;

    private final String clientIdentify;

    public ClientHandler(final Socket socket) {
        this.socket = socket;
        this.clientIdentify = socket.getInetAddress().getHostAddress() + ":" + socket.getPort();
    }

    @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")) {
                // 如果收到的是quit,就退出
                write2Client(printStream, "client will close");
                socket.close();
                break;
            }
            // 响应结果给客户端
            write2Client(printStream, "Server:" + received);
        }
    }

    /**
     * 将输入字节流封装成BufferedReader缓冲字符流
     * @param inputStream
     * @return
     */
    private BufferedReader wrap2Reader(InputStream inputStream) {
        return new BufferedReader(new InputStreamReader(inputStream));
    }

    /**
     * 将输出字节流封装成PrintStream
     * @param outputStream
     * @return
     */
    private PrintStream wrap2Print(OutputStream outputStream) {
        return new PrintStream(outputStream);
    }

    private void write2Client(PrintStream print, String message) {
        print.println(message);
        print.flush();
    }
}

当客户端发送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 (this.socket != null) {
            socket.close();
        }
    } catch (Throwable e) {
        // ignore
    }
}

当chat方法出现异常会被run方法捕获,在run方法中加入finally子句,用于执行客户端socket的主动关闭,为了使客户端的关闭不影响线程任务的结束,我们捕获了Throwable异常(在关闭客户端连接时出现的异常,视为不可恢复的异常,基本上没有针对该异常进行处理的必要)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值