簡體   English   中英

線程間共享資源,不同java版本的行為不同

[英]Sharing a resource among Threads, different behavior in different java versions

這是我第一次遇到類似下面的情況。

  • 多個線程(實現 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)。

要在多線程環境中訪問共享結構,您應該使用隱式或顯式鎖定來確保線程間的安全發布和訪問。 使用上面的代碼,它應該是這樣的:

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

注意:這種模式看起來很像生產者-消費者,最好使用隊列來實現。 LinkedBlockingQueue是一個很好的選擇,它提供了內置的並發控制功能。 這是在線程之間安全發布數據的好結構。 使用並發數據結構可以擺脫同步塊:

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

每次修改parallel region (具有多個並行運行的線程的區域)內的給定shared variable您必須確保mutual exclusion 您可以通過使用synchronizedlocks來保證 Java 中的mutual exclusion ,通常在需要更細粒度的同步時使用鎖。

如果程序只對給定的共享變量進行性能讀取,則不需要同步/鎖定對該變量的訪問。

由於您是該主題的新手,因此我向您推薦本教程

檢查返回

newCSRequests.add((CSRequest) message);

我猜它可能由於某種原因沒有被添加。 如果它是 HashSet 或類似的,可能是因為多個對象的哈希碼返回相同的值。 消息對象的 equals 實現是什么?

你也可以使用

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

以確保 arraylist 始終正確同步。

HTH

如果我做對了……至少有 2 個線程使用相同的共享數據結構。 你提到的數組..一個線程向數組添加值,如果數組的大小> 0,第二個線程“做事”。線程調度程序有可能運行第二個線程(檢查集合是否> 0),在第一個線程有機會運行並添加值之前。 從 bin 運行類或重新編譯它們無關。 如果您要從 bin 目錄再次運行該應用程序,您可能會再次看到該問題。 你運行了多少次應用程序? 它可能不會始終如一地重現,但在某一時刻您可能會再次看到該問題。

您可以以串行方式訪問數據結構,一次只允許一個線程訪問數組。 仍然不能保證第一個線程會運行,只有第二個線程才會檢查大小是否> 0。

根據您需要完成的工作,可能有更好的/其他方法來實現這一目標。 不一定使用數組來協調線程..

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM