Как наиболее эффективно синхронизировать коллекцию?

Пишу RESTful сервис на Spring. Имеется класс CarProviderService, его зона ответственности - обновление ArrayList'а cars.

Раз в минуту с предоставлением актуального состояние этого листа другим частям приложения. Это реализовано вызовом TimerTask'a в @PostConstruct методе.

Вопрос заключается в том:

как обеспечить предоставление актуального состояния ArrayList'а cars с максимальной производительностью?

В предоставленном коде, используется CopyOnWriteArrayList, но частые итерации по листу не будут синхронизированы, а обновление элементов будет осуществляться лишь раз в минуту.

И остался не решён вопрос с атомарностью методов cars.clear() и cars.adAll(newCars). При отсутствии явной синхронизации возможна ситуация когда другие потоки увидят пустой лист.

Получается, всё же необходимо явно синхронизировать блок очистки + заполнения данными листа и блок итерации по этому листу?

Буду благодарен предложенным решениям и советами, как это подобные ситуации обычно решаются на практике.

@Service 
public class CarProviderService {

    private static final Logger LOGGER = LoggerFactory.getLogger(CarProviderService.class);

    private List<Car> cars = new CopyOnWriteArrayList<>();


    /**
     *
     * @return instance of {@link CopyOnWriteArrayList<Car>} of cars
     * @param  company -  required company
     */
    public List<Car> getCarsByCompany(@NotNull Company company) {
        Predicate<Car> isBelongsToCompany = car -> car.getCompany().equals(company);
        return cars.stream().filter(isBelongsToCompany)
                .collect(Collectors.collectingAndThen(Collectors.toList(), CopyOnWriteArrayList::new));
    }

    /**
     * Updating cars every minute method.
     * {@link #cars} list contains all available cars.
     * Note that it's implementation is {@link CopyOnWriteArrayList<Car>}.
     * It provides an ability to iterate cars in thread-safe way.
     */
    @PostConstruct
    public void consumeActualCars(){

        TimerTask timerTask = new TimerTask() {

            @Override
            public void run() {
                LOGGER.debug("New cars fetching has been started");

                List<Car> newCars = new ArrayList<>();

                /* Заполняем лист newCars новыми данными, после чего обновляем cars*/

                cars.clear();
                cars.addAll(newCars);
            }
        };

        // Starting receiving new cars every minute
        Timer timer = new Timer();
        timer.schedule(timerTask,0,60000);
    }
}

Ответы (1 шт):

Автор решения: Roman Konoval

Самый простой и быстрый способ это изменить ссылку:

...
List<Car> newCars = new ArrayList<>();

/* Заполняем лист newCars новыми данными, после чего обновляем cars*/

cars = newCars;

Изменение ссылки атомарно и плюс нет накладных расходов на очистку/модификацию коллекции.

Важно только добавить модификатор volatile в определение поля cars чтобы изменение было видно в других потоках:

private volatile List<Car> cars = new CopyOnWriteArrayList<>();
→ Ссылка