如何通知多线程应用程序中的特定线程

时间:2017-05-15 13:40:22

标签: java multithreading rest sockets servletcontextlistener

我正在开发一个服务器应用程序,它从客户端接收RESTful请求并将其发送到新线程(UDP数据包)中的特定设备。此外,它还在执行开始时运行由servlet侦听器启动的另一个线程,该线程侦听从系统的所有设备发送的UDP数据包。

当客户端从特定设备发出请求时,REST服务必须启动一个线程,UDP数据包将从该线程发送到设备,并等待响应。当UDP服务器最终从该设备接收到数据包(从数据包中检查ip)时,它必须通知被阻塞的线程继续执行并完成。

我考虑过使用wait()notify()notifyAll()方法,但是,由于可以阻止多个线程等待多个设备的响应,我不知道如何我可以通知仅解锁所需的线程(在响应设备上发出请求的线程)。有没有办法用这种方法做到这一点?还有其他方法吗?这是一些代码(简化):

SocketServletListener:

public class SocketServletListener implements ServletContextListener {

    private UDPServer server;
    private ServletContext context;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        context = sce.getServletContext();  
        server = new UDPServer();
        server.start();
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        context = sce.getServletContext();
        server.interrupt();
    }

}

UDPServer:

public class UDPServer extends Thread {

    private SocketUDPCommunication comm;


    public UDPServer() {
        comm = new SocketUDPCommunication();
    }

    @Override
    public void run() {

        DatagramPacket response;
        try {
            comm.setPort(Utils.UDP_SERVER_PORT);
            comm.createSocket();

            while (!Thread.currentThread().isInterrupted()) {
                try {
                    response = comm.receiveResponse();
                } catch (SocketTimeoutException e) {
                    continue;
                }                           
                InetAddress ip = response.getAddress();
                int port = response.getPort();

                byte[] byteSend = comm.discardOffset(response);

                //TODO notify thread which made the request for the responding device (identified by ip)

            }
        } catch (IOException e) {
            System.err.println("Unable to process client request: " + e.getMessage());
        } catch (IllegalArgumentException ex) {
            System.err.println("Illegal Argument: " + ex.getMessage());
        } finally {
            comm.closeConnection();
        }
    }

    @Override
    public void interrupt() {
        super.interrupt();
        comm.closeConnection();
    }
}

DataSend.java:

@Path("dataSend")
public class DataSend {

    @Context
    private UriInfo context;

    public DataSend() {
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)   
    public Response postJson(ForceStatus status) {

        new TestExecution(status).start();

        return Response.status(Response.Status.OK).build();     
    }
}   

TestExecution:

public class TestExecution extends Thread {
    private ForceStatus status;

    public ExamExecution(ForceStatus status) {
        this.status = status;
    }

    @Override
    public void run() {
        ProtocolStatus p = new ProtocolStatus();
        byte[] s = p.createResponseFrame(status.getForce());

        List<Integer> executedTest = new ArrayList<>();

        //Simple UDP client
        UDPClient client = new UDPClient();
        .
        .
        .
        //t is a pojo which contains the data from a battery of tests
        while(!executedTest.contains(t.getTestId())) {

            client.send(status.getIp(), status.getPort(), s);
            //TODO wait until UDPServer thread gets the response from the device

            executedTest.add(t.getTestId());

            nextTest = t.getNextTestId();

            t = getEntity(nextTest);
        }       
    }
}

1 个答案:

答案 0 :(得分:0)

我解决了这个问题:

首先,我创建了一个单例类来管理由不同线程共享的请求列表。

public class SharedWaitingThreads {
    private ArrayList<ResponseToWait> queue;
    private static SharedWaitingThreads mySharedWaitingThreads;

    private SharedWaitingThreads() {
        queue = new ArrayList<>();
    }

    public static SharedWaitingThreads getInstance() {
        if(mySharedWaitingThreads == null)
            mySharedWaitingThreads = new SharedWaitingThreads();

        return mySharedWaitingThreads;
    }

    public ArrayList<ResponseToWait> getQueue() {
        return queue;
    }

    public void setQueue(ArrayList<ResponseToWait> queue) {
        this.queue = queue;
    }

    public void waitForAnswer(ResponseToWait r) throws InterruptedException {
        System.out.println("Petición registrada " + r.toString());
        synchronized(mySharedWaitingThreads) {
            mySharedWaitingThreads.getQueue().add(r);
            while(mySharedWaitingThreads.getQueue().contains(r)) {          
                mySharedWaitingThreads.wait();
            }
        }
    }



    public ResponseToWait answerWaitingThread(ResponseToWait r, boolean compareSeqNum) {
        ResponseToWait rw = null;
        synchronized(mySharedWaitingThreads) {
            for(ResponseToWait rwAux : mySharedWaitingThreads.getQueue()) {
                if(rwAux.equals(r)) {
                    rw = rwAux;
                    mySharedWaitingThreads.getQueue().remove(rwAux);
                    //every time a thread is released, notify to release the lock
                    mySharedWaitingThreads.notifyAll(); 
                    break;
                }
            }
        }
        return rw;
    }
}

这个单例实例由主线程(由contextInitialized)启动,并由需要等待响应以继续其工作的所有线程共享。 ResponseToWait包含每个请求/线程的所有必需信息。覆盖equals方法以适应所需的功能(在我的情况下,我通过ip和请求类型进行比较)

public class ExamExecution extends Thread {

    private SharedWaitingThreads waitingThreads;
    private static volatile Thread myThread;

    public ExamExecution(SharedWaitingThreads waitingThreads) {
        this.waitingThreads = waitingThreads;
    }

    @Override
    public void start() {
        myThread = new Thread(this);
        myThread.start();
    }

    @Override
    public void run() {
        Thread thisThread = Thread.currentThread();
        ProtocolStatus p = new ProtocolStatus();
        UDPClient client = new UDPClient();
        if(status.getWorkingMode() == WorkingMode.CONTINUE_EXECUTION) {
            byte[] frameRestart = p.createResponseFrame(status.getWorkingMode());
            client.send(getIp(), getPort(), frameRestart);
            //send the frame and block the thread until the server gets the proper response
            try {
                waitingThreads.waitForAnswer(new ResponseToWait(getIp(), getPort(), Utils.TYPE_STATUS, frameRestart));
            } catch (InterruptedException ex) {
                Logger.getLogger(ExamExecution.class.getName()).log(Level.SEVERE, null, ex);
            }
        }else
        if(status.getForce() == ForceStatus.START) {                
            //get data from database and initialize variables       
            .
            .
            .

            while(!executedTest.contains(testInExam.getTestId()) && myThread != null) {
                int attempts = 0;
                res = false;
                seqNumber = this.seqNumber.getValue();
                while(!res && (attempts < 3)) {
                    TestMemoryMap map = new TestMemoryMap(testInExam.getTestId());
                    byte[] frameConfig = pc.createConfigFrame(Utils.ID_RTU, (byte)1, (byte)0, 
                        Utils.MEM_MAP_VERSION, (byte)0, map.getMemoryMap().length, seqNumber, map.getMemoryMap());

                    res = client.send(getIp(), getPort(), frameConfig);

                    if(res) {
                        try {
                            System.out.println(Thread.currentThread().getName() + " blocked waiting config answer");
                            waitingThreads.waitForAnswer(new ResponseToWait(getIp(), getPort(), Utils.TYPE_CONFIG, frameConfig));
                        } catch (InterruptedException ex) {
                            Logger.getLogger(ExamExecution.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                    attempts++;
                }
                System.out.println("Config frame received:" + res);

                if(res) {
                    byte[] frame = p.createResponseFrame(status.getWorkingMode());
                    client.send(getIp(), getPort(), frame);

                    try {
                        System.out.println(Thread.currentThread().getName() + " blocked waiting end execution answer");
                        waitingThreads.waitForAnswer(new ResponseToWait(getIp(), getPort(), Utils.TYPE_STATUS, frame));
                    } catch (InterruptedException ex) {
                        Logger.getLogger(ExamExecution.class.getName()).log(Level.SEVERE, null, ex);
                    }               
                }
                //add test to list of executed tests
                executedTest.add(testInExam.getTestId());
                nextTest = testInExam.getNextTestInExamId();
                if(nextTest != 0) {
                    testInExam = daot.getEntity(nextTest);
                    testId = testInExam.getTestId();
                }
            }
        } else if(status.getForce() == ForceStatus.END) {
            System.out.println("Stopping...");
            //abort the execution of the thread
            this.endExecution();

        }
    }

    private void endExecution() {
        synchronized(myThread) {
            this.myThread = null;
        }   
    }
}

udp服务器线程必须回答等待线程的细节,具体取决于收到的响应:

public class UDPServer extends Thread {

    private SocketUDPCommunication comm;
    private UDPClient udpClient;
    private SharedWaitingThreads waitingThreads;

    public UDPServer(SharedWaitingThreads waitingThreads) {
        comm = new SocketUDPCommunication();
        udpClient = new UDPClient();
        this.waitingThreads = waitingThreads;
    }


    @Override
    public void run() {
        DatagramPacket response;
        try {
            comm.setPort(Utils.UDP_SERVER_PORT);
            comm.createSocket();

            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Waiting for clients to connect on port:" + comm.getSocket().getLocalPort());
                try {
                    response = comm.receiveResponse();
                } catch (SocketTimeoutException e) {
                    continue;
                }                           
                InetAddress ip = response.getAddress();
                int port = response.getPort();

                byte[] byteSend = comm.discardOffset(response);

                byte[] header = new byte[Utils.STD_HEADER_SIZE];
                Utils.getCleanHeader(byteSend, header);
                byte type = header[12];

                ResponseToWait r1;
                if(type == Utils.TYPE_CONFIG_REPORT) {
                    ProtocolConfig pc = new ProtocolConfig();
                    pc.parseFrame(byteSend);
                    int mapType = pc.getPayload()[0];
                    int idElement = pc.getPayload()[1];
                    r1 = new ResponseToWait(ip.getHostAddress(), port, Utils.TYPE_CONFIG, null);
                    if(checkPendingRequests(r1, null)) {
                        System.out.println("Resending config");
                        continue;
                    }
                    waitingThreads.answerWaitingThread(r1, true);
                }else if(type == Utils.TYPE_STATUS_REPORT) {
                    ProtocolStatus protocol = new ProtocolStatus();

                    r1 = new ResponseToWait(ip.getHostAddress(), port, Utils.TYPE_STATUS);
                    if(checkPendingRequests(r1, statusTest)) continue;
                    byte[] frame;
                    if(statusTest.equals(StatusTest.FINALIZED)) {
                        System.out.println("Test finalized. Waking threads");
                        r1 = new ResponseToWait(ip.getHostAddress(), port, Utils.TYPE_STATUS, null);
                        //Free possible waiting threads
                        ResponseToWait res1 = waitingThreads.answerWaitingThread(r1, false);

                    }
                }
            }
        } catch (IOException e) {
            System.err.println("Unable to process client request: " + e.getMessage());
        } catch (IllegalArgumentException ex) {
            System.err.println("Illegal Argument: " + ex.getMessage());
        } catch (InterruptedException ex) {
            Logger.getLogger(UDPServer.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            comm.closeConnection();
        }
    }

    private boolean checkPendingRequests(ResponseToWait rw, StatusTest status) {
        boolean resend = false;
        System.out.println("Status: " + status);
        synchronized(waitingThreads) {
            for(ResponseToWait r : waitingThreads.getQueue()) {
                if(r.getResponseType() == Utils.TYPE_CONFIG && r.getIp().equals(rw.getIp())) {
                    udpClient.send(r.getIp(), r.getPort(), r.getFrame());
                    resend = true;
                }
                if(r.getResponseType() == Utils.TYPE_STATUS && r.getIp().equals(rw.getIp())){
                    udpClient.send(r.getIp(), r.getPort(), r.getFrame());
                    resend = true;  
                }
            }
        }
        return resend;
    }

    @Override
    public void interrupt() {
        super.interrupt();
        comm.closeConnection();
    }

}

请注意,我简化了代码,试图让它变得更简单和教学,真实情况要复杂得多