Java 将打开的资源传递给线程是一种好的做法吗?

Java 将打开的资源传递给线程是一种好的做法吗?,java,multithreading,inputstream,Java,Multithreading,Inputstream,我有一个类MessageReader,它在一个单独的线程中从流中读取消息。现在,当我第一次创建解决方案时,我在thread类之外创建了stream对象,然后将其作为引用传递给MessageReader类。我通过我的自定义接口传递它,因为在某个时候我可能想更改流阅读器类。如果读卡器线程接收到一条指示消息流结束的特殊消息,它将优雅地结束。创建流的主线程关闭它。现在,我关心的是这是一个糟糕的设计?因为,如果我在读线程之外创建一个流,其他人可能会关闭它,这可能会导致问题。但是,另一方面,我也在读卡器线程

我有一个类
MessageReader
,它在一个单独的线程中从流中读取消息。现在,当我第一次创建解决方案时,我在thread类之外创建了stream对象,然后将其作为引用传递给
MessageReader
类。我通过我的自定义接口传递它,因为在某个时候我可能想更改流阅读器类。如果读卡器线程接收到一条指示消息流结束的特殊消息,它将优雅地结束。创建流的主线程关闭它。现在,我关心的是这是一个糟糕的设计?因为,如果我在读线程之外创建一个流,其他人可能会关闭它,这可能会导致问题。但是,另一方面,我也在读卡器线程之外创建了套接字,这有意义吗?因为,您可以创建更多线程来读取来自同一连接的消息。在读卡器线程外部还是内部创建想要的流,并让线程处理流的打开和关闭(但仍然具有获取不同流选项/对象的灵活性),这样做更好吗? 下面是我的示例类:

public class MessageReader implements Runnable {
    private MessageInputReader reader;
    /* Custom interface for reading messages */
    public MessageReader(MessageInputReader reader) {
        this.reader = reader;
    }

    public void run() {
        String line = null;
        try {
            while ((line = reader.readMessage()) != null) {
                if ("BYE".equals(line)) {
                    break;
                }
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}
public class ReaderExample {

    public static void main(String[] args) {
        Socket clientSocket = null;
        MessageBufferStream bufferedStream = null;
        MessageReader reader;
        try {
            clientSocket = new Socket("hostname", 4060);
            bufferedStream = new MessageBufferStream(clientSocket);
            reader = new MessageReader(bufferedStream);
            (new Thread(reader)).start();
            someBlockingMethodWhichDoesOtherWork();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bufferedStream != null)
                    bufferedStream.closeStream();
                if (clientSocket != null)
                    clientSocket.close();
            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
        }
    }
}
这是第二个选择:

public class MessageReader implements Runnable {
    private MessageBufferStream reader;

    public MessageReader(Socket clientSocket) throws IOException {
    /* I could probably create a factory method here which would
     * return different stream objects and achieve the same flexibility as in the first example? */
        this.reader = new MessageBufferStream(clientSocket);
    }

    public void run() {
        String line = null;
        try {
            while ((line = reader.readMessage()) != null) {
                if ("BYE".equals(line)) {
                    break;
                }
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        } finally {
            try {
                if (reader != null)
                    reader.closeStream();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
下面是代码的其余部分:

public interface MessageInputStream {
    public String readMessage() throws IOException;
    public void closeStream() throws IOException;
}

public class MessageBufferStream implements MessageInputStream {
    private BufferedReader reader;

    public MessageBufferStream(Socket clientSocket) throws IOException {
        this.reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
    }

    @Override
    public String readMessage() throws IOException {
        return reader.readLine();
    }

    @Override
    public void closeStream() throws IOException {
        reader.close();
    }
}
现在,当我第一次创建解决方案时,我在thread类之外创建了stream对象,然后将其作为引用传递给MessageReader类。。。我担心这是一个糟糕的设计

可能吧。您当然需要在线程外部创建
套接字
,但我认为在该套接字上启动流可能更好地在线程内部完成。主线程可能会保留对
套接字的引用(见下文),但分叉线程负责打开套接字上的流,并负责在消息完成后关闭流

因为,如果我在读线程之外创建一个流,其他人可能会关闭它,这可能会导致问题


当然,您可以编写主线程代码,以便它在出现问题时忘记流对象。主线程可能希望保留有关
Socket
的知识,这有时是个好主意。如果由于超时或应用程序正在关闭而要停止套接字上的处理,则主线程可以关闭套接字,这将导致IO方法抛出
IOException
,从而使线程有机会完全关闭。这是一个很好使用的模式。

这不是好的或坏的做法。与其说是实践问题,不如说是生命周期管理问题。如果您注意控制传递给线程的资源的管理,并确保它在使用之前是打开的,并且在仍然需要时从不关闭,那么您就没事了

文档是这里的最佳实践。弄清楚什么是预期的,什么是必须发生的,这样当你或其他人稍后看到它时,你就不会陷入麻烦。在您打开和关闭资源的位置,解释一下。在线程代码中或任何地方,记录前、后条件以及关于资源生命周期的任何假设。等等


现在,这当然假设了关于上述资源的一些事情。特别是,它是线程安全的,或者您正在注意不要通过其他方式(实际同步或简单的设计不变量)同时从多个线程访问它,并且所述资源可以在与构建它的线程不同的线程上使用(为此,您必须查阅它的文档或来源。例如,如果资源在构造时分配并初始化线程本地存储,您可能别无选择,只能在线程本身上打开它。

我不确定是否有一种既定的做法。我想我更希望工作线程完成所有工作,包括opening一个新的流,因此任何错误消息都来自一个点。但这是个人偏好,也是相当推测性的。我认为第二个选项是好的,因为一个实体控制资源,但这也是我的观点。我只是想知道对于这种情况是否有一种常见的做法。