簡體   English   中英

在多個線程之間傳遞 POJO(無易失性字段,同步方法)並確保更改可見性的正確方法?

[英]Correct way to pass a POJO (no volatile fields,synchronized methods) between multiple threads and assure visibility of changes?

我發現了很多與可見性、同步和線程相關的問題和答案,但似乎都沒有涵蓋我的特定用例(或者我可能只是不擅長搜索;-)所以我會問一個新問題並希望一些慷慨的靈魂會啟發我:)

我的問題是:鑒於下面的代碼,訪問主線程中 WorkItem 項的字段是否會正確反映線程池工作線程對它們所做的任何更改?

我的懷疑是“否”,因為這感覺類似於傳遞一個包含一些值的數組,只是在數組引用上同步,而不是在單個元素上同步...... JDK 中存在像 AtomicReferenceArray 這樣的類以及它們為什么存在一定是有原因的訪問單個元素時使用 getVolatile()/setVolatile()。

package com.voipfuture.voipmng.monitoring;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class IsThisThreadSafe
{
    public static class WorkItem { public String field1,field2; /* etc. */ }

    public static void main(String[] args) throws InterruptedException
    {
        final ExecutorService service = Executors.newFixedThreadPool(5);

        final List<WorkItem> items = List.of(new WorkItem(), new WorkItem());

        final CountDownLatch finished = new CountDownLatch(items.size());
        for (WorkItem item : items)
        {
            service.submit(() ->
            {
                try
                {
                    synchronized (item)
                    {
                        // mutate object
                        item.field1 = "test";
                    }
                }
                finally
                {
                    finished.countDown();
                }
            });
        }
        finished.await();

        for (WorkItem item : items)
        {
            // will this make sure all changes done inside
            // threadpool worker threads are visible here ?
            synchronized (item)
            {
                // do stuff with work item
                System.out.println(item.field1);
            }
        }
    }
}

如果所有線程對共享 object 的所有訪問(讀取和寫入)都在同步塊中完成,則訪問是安全的。 同步塊添加了 memory 屏障,因此任何等待寫入主 memory 的修改都會被提交。

鑒於下面的代碼,訪問主線程中 WorkItem 項的字段是否會正確反映線程池工作線程對它們所做的任何更改?

答案實際上是肯定的,你會的。 確保可視性有兩個原因。

  1. synchronized(item) :假設你沒有CountDownLatch (更多關於下一個),但你只有synchronized(item)在那里。 由於您正在同步同一實例的讀取和寫入,因此讀取線程將看到寫入之前發生的更改。 但是,如果沒有發生寫入,那么您將看不到值test ,您只會看到null 因此,即使它保證 memory 可見性,它也不能保證程序順序。

  2. finished.await(); 現在讓我們假設您刪除了synchronized(item) 如果您所擁有的只是finished.countDown()finished.await() ,那么您保證在await繼續之前發生的任何更新現在在退出await之后都是可見的。 因此,只需使用CountDownLatch ,您就可以為程序提供 memory 可見性和並發程序順序。

來自CountDownLatch javadocs

Memory 一致性效果:在計數達到零之前,線程中的操作在調用 countDown() 之前發生在從另一個線程中的相應 await() 成功返回之后的操作。

暫無
暫無

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

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