Правильный способ передачи POJO (нет изменяемых полей, синхронизированных методов) между несколькими потоками и обеспечить видимость изменений? - PullRequest
2 голосов
/ 29 апреля 2020

Я нашел много вопросов и ответов, связанных с видимостью, синхронизацией и многопоточностью, но ни один из них не отвечал моему конкретному варианту использования (или, может быть, я просто отстой в поиске ;-), поэтому я задам новый вопрос и надеюсь, что какая-то щедрая душа просветит меня:)

Мой вопрос: учитывая приведенный ниже код, будет ли доступ к полям элементов WorkItem в главном потоке правильно отражать любые изменения, внесенные в них рабочими потоками пула потоков?

Мое подозрение - "НЕТ", поскольку это похоже на передачу массива некоторых значений вокруг и просто синхронизацию по ссылке на массив, но не по отдельным элементам ... должна быть причина, по которой существуют классы, такие как AtomicReferenceArray в JDK и почему они используют 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);
            }
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 29 апреля 2020

Учитывая приведенный ниже код, будет ли доступ к полям элементов WorkItem в основном потоке правильно отражать любые изменения, внесенные в них рабочими потоками пула потоков?

Ответ на самом деле да, ты будешь. Есть две причины, по которым вы гарантируете жизнеспособность.

  1. synchronized(item): Допустим, у вас нет CountDownLatch (подробнее об этом далее), но у вас просто есть synchronized(item). Поскольку вы синхронизируете обе операции чтения и записи одного и того же экземпляра , поток чтения увидит изменения, которые произошли до записи. Однако , если записи не происходили, вы не увидите значение test, вы просто увидите null. Таким образом, хотя он и гарантирует видимость памяти, он не гарантирует порядок программ.

  2. finished.await(); Теперь давайте предположим, что вы удалили synchronized(item). Если все, что у вас было finished.countDown() и finished.await(), вы гарантируете, что любые обновления, которые произошли до продолжения await, теперь будут видны после выхода из await. Поэтому, просто используя CountDownLatch, вы даете своей программе и видимость памяти, и параллельный порядок программ.

От CountDownLatch javadocs

Эффекты согласованности памяти: пока счетчик не достигнет нуля, действия в потоке перед вызовом countDown () действия, предшествующие успешному возвращению соответствующего await () в другом потоке.

0 голосов
/ 29 апреля 2020

Если весь доступ (как чтение, так и запись) к общему объекту осуществляется в синхронизированном блоке всеми потоками, то доступ безопасен. Синхронизированный блок добавляет барьер памяти, поэтому любые изменения, ожидающие записи в основную память, фиксируются.

...