Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何通过持续监听套接字来释放线程持有的互斥锁,而线程从不释放互斥锁_Java_Multithreading_Sockets_Synchronized_Java 14 - Fatal编程技术网

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
中。