多线程线程重启

时间:2016-09-04 08:17:58

标签: java multithreading performance

我正在编写一个小应用程序来远程控制(Behringer x32)混音控制台。我遇到了沟通问题。

我正在将数据从pc(app)发送到控制台(端口10023 UDP协议),然后控制台从pc端口回复发送数据(随机端口)。 所以我有两个线程用于发送数据,一个用于从控制台监听数据.....所以每次我将数据发送到控制台时,我需要更改监听端口...所以我必须杀死听取线程并开始新的。

但是经过一段时间后,有一个和应用程序有大约x1000个线程打开。

如何在不创建新线程的情况下重新启动线程或更新侦听端口?

这里是本节的代码,整个文件是@ gihub

监听线程类:

public class Receiver implements Runnable {

    private List<IReceiverListener> listeners;
    private final static int PACKETSIZE = 48;
    private int port;

    public Receiver() {
        listeners = new ArrayList();
    }

    public void addReceiverListener(IReceiverListener listener) {
        listeners.add(listener);
    }

    private void update(String data, String adress) {
        for (IReceiverListener listener : listeners) {
            listener.receiveConsoleData(data, adress);
            if (data.indexOf("active") > -1) {
                listener.incrementWatchDog();
            }
        }
    }

    @Override
    public void run() {
        try {
            // Convert the argument to ensure that is it valid
            // Construct the socket
            while (true) {
                //System.out.println("Listen on Port:" + this.port);
                DatagramSocket socket = new DatagramSocket(this.port);
                // Create a packet
                DatagramPacket packet = new DatagramPacket(new byte[PACKETSIZE], PACKETSIZE);
                // Receive a packet (blocking)
                socket.receive(packet);
                // Print the packet
                update(new String(packet.getData()), packet.getAddress().toString());
                //logger.addLogData(new String(packet.getData())+"  "+packet.getAddress().toString());
                // Return the packet to the sender
                socket.close();
            }
        } catch (IOException e) {

        }
    }


    public void setPort(int port) {
        this.port = port;
    }

    public int getPort() {
        return port;
    }
}

这里是我的端口updateFunction

 @Override
public void updatePort(int port) {
    receiverThread.interrupt();
    receiverThread = null;
    receiver.setPort(port);
    receiverThread = new Thread(receiver);
    receiverThread.start();
}

并且发送线程在发送数据时执行此操作:

 listener.updatePort(dsocket.getLocalPort());

2 个答案:

答案 0 :(得分:2)

这实际上不是线程问题。问题是,接收器线程卡在receive方法中,因此无法对更改的端口做出反应。但是,从另一个线程调用方法DatagramSocket#close会释放阻塞接收器线程SocketException

因此,您可以通过在接收端口更改时关闭当前接收的套接字来解决此问题。接收线程现在可以捕获SocketException并创建一个侦听新端口的新DatagramSocket

无需杀死并重新创建线程。

首先将套接字放入字段中。这允许您从另一个线程访问它,因此您可以调用socket.close()方法。其次,你将另一个try-catch块放入while(true)循环中,该循环只捕获SocketException

这样的事可能会正常工作:

public class Receiver implements Runnable {

    private static final int PACKETSIZE = 48;

    private final ConcurrentLinkedQueue<IReceiverListener> listeners = new ConcurrentLinkedQueue<>();

    private volatile DatagramSocket socket;
    private volatile int port;

    public Receiver(int port) {
        this.port = port;
    }

    public void addReceiverListener(IReceiverListener listener) {
        listeners.add(listener);
    }

    public void updatePort(int port) {
        this.port = port;
        DatagramSocket socket = this.socket;
        if (socket != null) {
            socket.close();
        }
    }

    @Override
    public void run() {
        try {
            while (true) {
                receiveLoop(new DatagramSocket(port));
            }
        } catch (IOException e) {
            // handle error
        }
    }

    private void receiveLoop(DatagramSocket newSocket) throws IOException {
        try (DatagramSocket socket = newSocket) {
            this.socket = newSocket;
            while (true) {
                DatagramPacket packet = new DatagramPacket(new byte[PACKETSIZE], PACKETSIZE);
                socket.receive(packet);
                process(packet);
            }
        } catch (SocketException e) {
            // port was changed -> return and restart with a new socket
        } finally {
            this.socket = null;
        }
    }

    private void process(DatagramPacket packet) {
        update(new String(packet.getData()), packet.getAddress().toString());
    }

    private void update(String data, String adress) {
        for (IReceiverListener listener : listeners) {
            listener.receiveConsoleData(data, adress);
            if (data.indexOf("active") > -1) {
                listener.incrementWatchDog();
            }
        }
    }
}

请注意,这可能仍然包含一些错误。它只能让你大致了解如何解决这个问题。

答案 1 :(得分:1)

当您使用DatagramSocket时,可以通过绑定将套接字更改为新端口而不是使用的端口:

socket.bind(new InetSocketAddress(new_port));

但请记住bind()方法除非套接字已经打开并且分配了一个端口,否则不会起作用,所以第一次必须定期创建套接字,然后当你尝试更改时端口,只需绑定它。

以下是该过程的完整可视化:

public void video_udp_server(int port) throws Exception
{
    byte[] receiveData = new byte[Integer.MAX_VALUE/100];
    for(int i = 0; i < receiveData.length; i++){
        receiveData[i] = ' ';
    }

    DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
    DatagramSocket socket = null;
    try{
        socket = new DatagramSocket(port);
    }catch(Exception ex){
        socket.bind(new InetSocketAddress(port));
    }
    socket.setReuseAddress(true);
    socket.receive(receivePacket);

    System.out.println(new String(receivePacket.getData()));

}