ObjectInputStream 读线程阻塞 ObjectOutputStream 写线程

ObjectInputStream read thread is blocking ObjectOutputStream writing thread

我有两个线程,我们称它们为 ABA 不断地通过 readPacket 函数寻找来自 ObjectInputStream 的数据包(这将是线程中的 while(true) 等)

虽然 A 正在寻找这些数据包,但我希望 B 通过 writePacket 函数将数据包写入 ObjectOutputStream

但是每当我想这样做时,我都会陷入僵局;我不明白两个不同的函数如何相互死锁?

   import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;

public class ConnectionBay
{
    private Socket connection;
    private ObjectOutputStream output_stream;
    private ObjectInputStream input_stream;

    ConnectionBay(Socket socket) throws IOException{
        this.connection = socket;
        this.output_stream = new ObjectOutputStream(connection.getOutputStream());
        this.output_stream.flush();
        this.output_stream.reset();
        this.input_stream = new ObjectInputStream(connection.getInputStream());
    }
    public synchronized void writePacket(Packet packet) throws IOException{
        this.output_stream.writeObject(packet);
        this.output_stream.flush();
        this.output_stream.reset();
    }
    public synchronized Packet readPacket() throws IOException, ClassNotFoundException{
        return (Packet)this.input_stream.readObject();
    }
}

由于您在 readPacket 和 writePacket 定义中使用了 synchronized 关键字,因此您已经创建了同步方法。如本 中所述,对同一对象的同步方法的两次调用不可能交错。在您的情况下,“对象”不是输入或输出流,而是 ConnectionBay 对象。通常,当您想阻止两个线程访问对象中的任何状态时,您会使用同步方法。由于这两个流都是单个 ConnectionBay 对象的一部分,并且同步方法阻止了当前对该对象的访问,因此读写之间的死锁正是我所期望的。

根据您的问题 - 听起来您真正想要的是有两个单独的同步 - 一个用于输入流,一个用于输出流。如果是这种情况,我建议使用 synchronized statements。下面的示例显示了如何修改 readPacket。

public Packet readPacket() throws IOException, ClassNotFoundException{
    synchronized(this.input_stream) {
        return (Packet)this.input_stream.readObject();
    }
}

如果你采用这种方法(让流本身成为锁),你应该将“input_stream”变量设置为 final

private final ObjectInputStream input_stream;

这确保您可以安全地使用 input_stream 作为锁,因为它保证在构造后不会更改。

另一种方法是再创建两个变量作为锁并将它们与流配对。

private ObjectInputStream input_stream;
private final Object input_lock = new Object();
private ObjectOutputStream output_stream;
private final Object output_lock = new Object();

...

public Packet readPacket() throws IOException, ClassNotFoundException{
    synchronized(this.input_lock) {
        return (Packet)this.input_stream.readObject();
    }
}

这种方法更灵活,如果您在连接间隔的生命周期内创建和销毁流变量,则可能是必要的。

不要忘记用类似的更改更新您的 writePacket 方法。