Java 如何通过持续监听套接字来释放线程持有的互斥锁,而线程从不释放互斥锁
有两个类Java 如何通过持续监听套接字来释放线程持有的互斥锁,而线程从不释放互斥锁,java,multithreading,sockets,synchronized,java-14,Java,Multithreading,Sockets,Synchronized,Java 14,有两个类Client和ChatWindow,客户端有DatagramSocket,InetAddress和端口字段,以及发送、接收和关闭套接字的方法。要关闭套接字,我使用匿名线程“socketCLOSE” 客户端类 public class Client { private static final long serialVersionUID = 1L; private DatagramSocket socket; private String name, address; private
Client
和ChatWindow
,客户端有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
类是一种GUI,它扩展了JPanel
并实现了Runnable
,类中有两个线程-run
和Listen
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
状态:在java.net上被阻止。DatagramSocket@1de1602拥有者:听着
已阻止的总数:1等待的总数:0
堆栈跟踪:
app//com.server.Client$2.run(Client.java:90)
姓名:收听
状态:可运行
已阻止的总数:0等待的总数:0
堆栈跟踪:
爪哇。base@14.0.1/java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData(本机方法)
JAVAbase@14.0.1/java.net.DualStackPlainDatagramSocketImpl.receive0(DualStackPlainDatagramSocketImpl.java:130)
- 锁定java.net。DualStackPlainDatagramSocketImpl@3dd26cc7 JAVAbase@14.0.1/java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:181)
- 锁定java.net。DualStackPlainDatagramSocketImpl@3dd26cc7 JAVAbase@14.0.1/net.DatagramSocket.receive(DatagramSocket.java:864)
- 锁定java.net。DatagramPacket@6d21ecb
- 锁定java.net。DatagramSocket@1de1602 app//com.thecherno.chernochat.Client.receive(Client.java:59) app//com.thecherno.chernochat.ClientWindow$5.run(ClientWindow.java:183)
这不允许SocketCLOSE线程关闭
synchronized
块内的套接字,因为套接字上的锁由侦听线程持有。如何使侦听线程释放其锁,程序在不关闭套接字的情况下终止,调试器将侦听线程显示为仍然可运行。实现本身有缺陷吗?或者这可以解决吗?我可以用JDK 14重现这个问题,但不能用JDK 15或更高版本
这似乎是合理的,因为这表明已经为JDK15重写了实现。该报告甚至说,“该实现还存在一些并发问题(例如,异步关闭),需要彻底检查才能正确解决。”
然而,您也可以摆脱JDK14的问题;只需删除已同步的
。文档中没有说明调用close()
需要同步,当我删除它时,我的测试用例按预期工作
当您想要协调对应用程序套接字的多线程访问时,应该使用与套接字实例本身不同的锁对象。非常感谢。我认为这可能没有得到响应,是的,删除synchornized关闭了套接字,但这样做会导致SOCKETCLOSED异常,位于客户机接收方法中的socket.received()。“当您想要协调对应用程序套接字的多线程访问时,您应该使用与套接字实例本身不同的锁对象。”除了套接字之外,我在这里还可以使用什么对象?客户端对象?。由于
socket
变量是在Client
中声明的,因此在Client
实例上进行同步将是一个很好的约定。但是,不清楚是否有必要(您没有显示socket
的分配位置)。这里有一些奇怪的东西,比如“Running”
线程,它唯一的操作就是创建“Listen”
线程。此外,您不应该捕获IOException
并像什么都没发生一样继续操作,尤其是在返回无效数据的receive
中。