如何通过持续侦听套接字释放线程持有的互斥锁,该锁从不让它离开

问题描述

ClientChatWindow 两个类,客户端有 DatagramSocketInetAddress 和端口字段以及发送、接收和关闭套接字的方法。要关闭套接字,我使用匿名线程“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,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 上被 BLOCKED 拥有者:Listen 总阻塞:1 总等待:0

堆栈跟踪: app//com.server.Client$2.run(Client.java:90)

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

堆栈跟踪:

[email protected]/java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData(本机方法) [email protected]/java.net.DualStackPlainDatagramSocketImpl.receive0(DualStackPlainDatagramSocketImpl.java:130)

  • 锁定 java.net.DualStackPlainDatagramSocketImpl@3dd26cc7 [email protected]/java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:181)
  • 锁定 java.net.DualStackPlainDatagramSocketImpl@3dd26cc7 [email protected]/java.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 块内的套接字,因为 侦听线程 持有套接字上的锁。我怎样才能让监听线程释放它的锁,程序在不关闭套接字的情况下终止,调试器显示监听线程仍然可以运行。是实现本身有缺陷还是可以解决?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)