在线程之间共享资源,在不同的Java版本中使用不同的行为

pha*_*ani 4 java parallel-processing multithreading javac thread-safety

这是我第一次遇到类似下面的内容.

  • 多个线程(实现Runnable的内部类)共享数据结构(上层类的实例变量).

  • 工作:从Eclipse项目的bin文件夹中获取类,在Unix机器上运行.

  • 不工作:直接在Unix机器上编译src并使用那些类文件.代码编译然后运行时没有错误/警告,但是一个线程无法正确访问共享资源.

  • 问题:一个线程将元素添加到上面的公共DS中.第二个线程执行以下操作...

    while(true){
     if(myArrayList.size() > 0){
     //do stuff
     }
    
    Run Code Online (Sandbox Code Playgroud)

    }

  • 日志显示大小在线程1中更新.

  • 出于某种神秘的原因,工作流程并没有因为()......

如果我直接粘贴Eclipse的bin文件夹中的类文件,则完全相同的代码运行完美.

如果我错过任何明显的事,我道歉.

码:

ArrayList<CSRequest> newCSRequests = new ArrayList<CSRequest>();
Run Code Online (Sandbox Code Playgroud)

//线程1

private class ListeningSocketThread implements Runnable {
    ServerSocket listeningSocket;

    public void run() {
        try {
            LogUtil.log("Initiating...");
            init(); // creates socket
            processIncomongMessages();
            listeningSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void processIncomongMessages() throws IOException {     
        while (true) {
            try {
                processMessage(listeningSocket.accept());
            } catch (ClassNotFoundException e) {                    
                e.printStackTrace();
            }
        }
    }

    private void processMessage(Socket s) throws IOException, ClassNotFoundException {
        // read message
        ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
        Object message = ois.readObject();
        LogUtil.log("adding...: before size: " + newCSRequests.size());
        synchronized (newCSRequests) {
                newCSRequests.add((CSRequest) message);
        }
        LogUtil.log("adding...: after size: " + newCSRequests.size()); // YES, THE SIZE IS UPDATED TO > 0
        //closing....

    }

........
Run Code Online (Sandbox Code Playgroud)

}

//Thread 2
private class CSRequestResponder implements Runnable {

        public void run() {
            LogUtil.log("Initiating..."); // REACHES..
            while (true) {
//              LogUtil.log("inside while..."); // IF NOT COMMENTED, FLOODS THE CONSOLE WITH THIS MSG...
                if (newCSRequests.size() > 0) { // DOES NOT PASS
                    LogUtil.log("inside if size > 0..."); // NEVER REACHES....
                    try {
                        handleNewCSRequests();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
....
}
Run Code Online (Sandbox Code Playgroud)

更新非常 感谢@maasg ...

解决方法是在检查线程2中的大小之前添加synchronized(myArrayList).

maa*_*asg 5

要在多线程环境中访问共享结构,您应该使用隐式或显式锁定来确保线程之间的安全发布和访问.使用上面的代码,它应该如下所示:

while(true){
    synchronized (myArrayList) {
        if(myArrayList.size() > 0){
            //do stuff
        }
    }
    //sleep(...) // outside the lock!
}
Run Code Online (Sandbox Code Playgroud)

注意:此模式看起来很像生产者 - 消费者,并且使用队列更好地实现.LinkedBlockingQueue是一个很好的选择,并提供内置的并发控制功能.它是在线程之间安全发布数据的良好结构.使用并发数据结构可以摆脱synchronized块:

Queue queue = new LinkedBlockingQueue(...)
...
while(true){
        Data data = queue.take(); // this will wait until there's data in the queue
        doStuff(data);
}
Run Code Online (Sandbox Code Playgroud)