如何通过持续侦听套接字来释放线程持有的永不释放的互斥锁



ClientChatWindow两个类,客户端有DatagramSocket,InetAddress和端口字段以及发送,接收和关闭套接字的方法。要关闭套接字,我使用匿名线程"socketCLOSE">

客户机类

public class Client {
private static final long serialVersionUID = 1L;
private DatagramSocket socket;
private String name, address;
private int port;
private InetAddress ip;
private Thread send;
private int ID = -1;
private boolean flag = false;
public Client(String name, String address, int port) {
this.name = name;
this.address = address;
this.port = port;
}

public String receive() {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {

socket.receive(packet);

} catch (IOException e) {
e.printStackTrace();
}
String message = new String(packet.getData());
return message;
}
public void send(final byte[] data) {
send = new Thread("Send") {
public void run() {
DatagramPacket packet = new DatagramPacket(data, data.length, ip, port);
try {

socket.send(packet);

} catch (IOException e) {
e.printStackTrace();
}
}
};
send.start(); 
}
public int close() {
System.err.println("close function called");
new Thread("socketClOSE") {
public void run() {
synchronized (socket) {
socket.close();
System.err.println("is socket closed "+socket.isClosed());
}
}
}.start(); 

return 0;
}

ChatWindow类是一种扩展JPanel并实现Runnable的GUI,类中有两个线程——runListen

public class ClientWindow extends JFrame implements Runnable {
private static final long serialVersionUID = 1L;
private Thread run, listen;
private Client client;
private boolean running = false;
public ClientWindow(String name, String address, int port) {

client = new Client(name, address, port);

createWindow();
console("Attempting a connection to " + address + ":" + port + ", user: " + name);
String connection = "/c/" + name + "/e/";
client.send(connection.getBytes());

running = true;
run = new Thread(this, "Running");
run.start();
}
private void createWindow() {

{
//Jcomponents and Layouts here
}

addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
String disconnect = "/d/" + client.getID() + "/e/";
send(disconnect, false);
running = false;
client.close();
dispose();
}
});
setVisible(true);
txtMessage.requestFocusInWindow();
}
public void run() {
listen();
}
private void send(String message, boolean text) {
if (message.equals("")) return;
if (text) {
message = client.getName() + ": " + message;
message = "/m/" + message + "/e/";
txtMessage.setText("");
}
client.send(message.getBytes());
}
public void listen() {
listen = new Thread("Listen") {
public void run() {
while (running) {
String message = client.receive();
if (message.startsWith("/c/")) {
client.setID(Integer.parseInt(message.split("/c/|/e/")[1]));
console("Successfully connected to server! ID: " + client.getID());
} else if (message.startsWith("/m/")) {
String text = message.substring(3);
text = text.split("/e/")[0];
console(text);
} else if (message.startsWith("/i/")) {
String text = "/i/" + client.getID() + "/e/";
send(text, false);
} else if (message.startsWith("/u/")) {
String[] u = message.split("/u/|/n/|/e/");
users.update(Arrays.copyOfRange(u, 1, u.length - 1));
}
}

}
};
listen.start();
}
public void console(String message) {
}
}

每当客户端关闭时,调用client.close(),产生socketCLOSE线程,但是线程什么也不做,它进入阻塞状态,如堆栈跟踪所示-

名称:socketClOSE状态:BLOCKED在java.net.DatagramSocket@1de1602上,由:Listen所有阻塞总数:1等待总数:0

堆栈跟踪:app//com.server.Client运行2.美元(Client.java: 90)

名称:听状态:可运行总阻塞:0总等待:0

堆栈跟踪:

java.base@14.0.1/java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData(本地方法)java.base@14.0.1/java.net.DualStackPlainDatagramSocketImpl.receive0 (DualStackPlainDatagramSocketImpl.java: 130)

  • 锁java.net.DualStackPlainDatagramSocketImpl@3dd26cc7java.base@14.0.1/java.net.AbstractPlainDatagramSocketImpl.receive (AbstractPlainDatagramSocketImpl.java: 181)
  • 锁java.net.DualStackPlainDatagramSocketImpl@3dd26cc7java.base@14.0.1/java.net.DatagramSocket.receive (DatagramSocket.java: 864)
  • 锁定java.net.DatagramPacket@6d21ecb
  • 锁java.net.DatagramSocket@1de1602应用程序//com.thecherno.chernochat.Client.receive (Client.java: 59)app//com.thecherno.chernochat.ClientWindow运行5.美元(ClientWindow.java: 183)

这不会让SocketCLOSE线程关闭synchronized块中的套接字,因为套接字上的锁被持有。. 如何使侦听线程释放其锁,程序终止而不关闭套接字和调试器显示侦听线程仍然可运行。是实现本身有缺陷还是可以解决?

我可以在JDK 14中重现这个问题,但不能在JDK 15或更新的版本中重现。

这似乎是合理的,因为JDK-8235674, JEP 373: reimplementation the Legacy DatagramSocket API表明已经为JDK 15重写了实现。该报告甚至说">实现还存在几个并发性问题(例如,异步关闭),需要彻底检查才能正确解决。">

但是,你也可以在JDK 14中解决这个问题;只要去掉synchronized。文档中没有说明调用close()需要同步,当我删除它时,我的测试用例按预期工作。

当你想协调多线程访问你的应用程序的套接字时,你应该使用一个不同于套接字实例本身的锁对象。

最新更新