【问题标题】:Sharing a resource among Threads, different behavior in different java versions在线程之间共享资源,不同java版本中的不同行为
【发布时间】:2012-10-16 10:19:57
【问题描述】:

这是我第一次遇到下面这样的事情。

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

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

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

  • 问题:一个线程将元素添加到上述常见的 DS。第二个线程执行以下操作...

      while(true){
       if(myArrayList.size() > 0){
       //do stuff
       }
    

    }

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

  • 由于某种神秘的原因,工作流程没有进入 if() ...

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

如果我遗漏了任何明显的东西,我深表歉意。

代码:

ArrayList<CSRequest> newCSRequests = new ArrayList<CSRequest>();

//线程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....
        
    }
    
........

}

//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();
                    }
                }
            }
        }
....
}

更新

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

【问题讨论】:

    标签: java multithreading parallel-processing thread-safety javac


    【解决方案1】:

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

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

    注意:这个模式看起来很像producer-consumer,使用队列更好地实现。 LinkedBlockingQueue 是一个很好的选择,它提供了内置的并发控制功能。这是线程间安全发布数据的良好结构。 使用并发数据结构可以让你摆脱同步块:

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

    【讨论】:

      【解决方案2】:

      每次您在parallel region(一个具有多个并行运行的线程的区域)内修改给定的shared variable 时,您必须确保mutual exclusion。您可以使用synchronizedlocks 在Java 中保证mutual exclusion,通常在需要更细粒度的同步时使用锁。

      如果程序只对给定的共享变量进行性能读取,则不需要同步/锁定对该变量的访问。

      由于您是该主题的新手,我向您推荐tutorial

      【讨论】:

        【解决方案3】:

        如果我做对了.. 至少有 2 个线程使用相同的共享数据结构。你提到的数组..如果数组的大小> 0,一个线程将值添加到数组中,第二个线程“做事”。 在第一个线程有机会运行并添加值之前,线程调度程序有可能运行第二个线程(检查集合是否 > 0)。 从 bin 运行类或重新编译它们无关。如果您要从 bin 目录再次运行该应用程序,您可能会再次看到该问题。您运行该应用程序多少次? 它可能无法始终如一地重现,但有时您可能会再次看到该问题。

        您可以以串行方式访问数据结构,一次只允许一个线程访问数组。但这并不能保证第一个线程会运行,只有第二个线程才会检查大小是否 > 0。

        根据您需要完成的任务,可能有更好的/其他方式来实现。不一定使用数组来协调线程..

        【讨论】:

          【解决方案4】:

          检查返回

          newCSRequests.add((CSRequest) message);
          

          我猜它可能由于某种原因没有被添加。如果它是 HashSet 或类似的,可能是因为多个对象的哈希码返回相同的值。消息对象的equals实现是什么?

          你也可以使用

          List list = Collections.synchronizedList(new ArrayList(...));
          

          确保数组列表始终正确同步。

          HTH

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-02-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-18
            相关资源
            最近更新 更多