понимание синхронизированного списка в Java - PullRequest
1 голос
/ 25 мая 2019

У меня есть ArrayList, который изменяется одним потоком и читается другим. Читающая ветка с удовольствием читает все, что осталось в списке после внесения изменений. Он должен остановиться, когда все элементы будут удалены из списка. В java-документе Collections.synchronizedList () упоминается, что мы должны вручную синхронизировать список перед выполнением каких-либо операций с ним.

Обязательно, чтобы пользователь вручную синхронизировался на возвращенном список при итерации по нему:

Список списка = Collections.synchronizedList (new ArrayList ()); ... синхронизированный (список) { Iterator i = list.iterator (); // Должен быть в синхронизированном блоке while (i.hasNext ()) Foo (i.next ()); } Несоблюдение этого совета может привести к недетерминированному поведению.

но при синхронизации он продолжает чтение до конца данных, прежде чем дает возможность другому потоку изменить список. (Да, потому что список заблокирован).

package test.thread.concurrency.synchronizedlist;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class Solution {
     private List<String> list = Collections.synchronizedList(new ArrayList<String>());
    //private List<String> list = new ArrayList<>();
    // private CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

    public Solution() {
        for (int i = 0; i < 30; i++) {
            list.add("Item_" + i);
        }
    }

    private void removeItemsFromList() {
        synchronized (list) {
            System.out.println("Removing: " + list.remove(0));
        }
    }

    private void readFromLIst() {

        synchronized (list) {
            Iterator<String> it = list.iterator();
            while (it.hasNext()) {
                System.out.println("READ: "+ it.next());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public static void main(String[] args) throws InterruptedException {
        final Solution issue = new Solution();

        Thread t1 = new Thread(() -> {
            issue.readFromLIst();
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                issue.removeItemsFromList();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });

        t2.start();
        t1.start();


        t1.join();
        t2.join();

        System.out.println("Final List: " + issue.list);

    }

}

Удаление: Item_0 READ: Item_1 READ: Item_2 READ: Item_3 READ: Item_4 ЧИТАТЬ: Item_5 READ: Item_6 READ: Item_7 READ: Item_8 READ: Item_9 READ: Item_10 READ: Item_11 READ: Item_12 READ: Item_13 READ: Item_14 READ: Item_15 READ: Item_16 READ: Item_17 READ: Item_18 READ: Item_19 READ: Item_20 READ: Item_21 READ: Item_22 READ: Item_23 ​​READ: Item_24 READ: Item_25 READ: Item_26 READ: Item_27 READ: Item_28 READ: Item_29 Удаление: Item_1 Удаление: Item_2 Удаление: Item_3 Удаление: Item_4 Удаление: Item_5 Удаление: Item_6 Удаление: Item_7 Удаление: Item_8 Удаление: Item_9 Удаление: Item_10 Удаление: Item_11 Удаление: Item_12 Удаление: Item_13 Удаление: Item_14 Удаление: Item_15 Удаление: Item_16 Удаление: Item_17 Удаление: Item_18 Удаление: Item_19 Удаление: Item_20 Удаление: Item_21 Удаление: Item_22 Удаление: Item_23 ​​Удаление: Item_24 Удаление: Item_25 Удаление: Item_26 Удаление: Item_27 Удаление: Item_28 Удаление: Item_29 Окончательный список: []

Я получил его, используя CopyOnWriteArrayList (без синхронизированный блок ), но, похоже, возникают накладные расходы на создание нового списка при каждом изменении списка.

Я что-то упускаю при использовании sychronizedList для достижения того же результата, что и в CopyOnWriteArrayList?

1 Ответ

1 голос
/ 25 мая 2019

Для synchronizedList вы должны удерживать замок во время его обхода.

Но так как вы выполняете только удаление-сначала и обход в списке, вы можете вместо этого использовать ConcurrentLinkedDeque. Из документа ConcurrentLinkedDeque:

Итераторы слабо согласованы, возвращая элементы, отражающие состояние deque в некоторой точке в момент или после создания итератора. Они не генерируют исключение ConcurrentModificationException и могут выполняться одновременно с другими операциями.

Таким образом, вы можете пройти по деку без блокировки, пока другой поток его модифицирует.

import java.util.Deque;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedDeque;

public class Solution {
    private Deque<String> list = new ConcurrentLinkedDeque<>();

    public Solution() {
        for (int i = 0; i < 30; i++) {
            list.add("Item_" + i);
        }
    }

    private void removeItemsFromList() {
        String item = list.removeFirst();
        System.out.println("REMOVE " + item);
    }

    private void readFromLIst() {
        boolean isEmpty;
        do {
            isEmpty = true;
            Iterator<String> it = list.iterator();
            while (it.hasNext()) {
                System.out.println("READ " + it.next());
                isEmpty = false;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } while (!isEmpty);

    }

    public static void main(String[] args) throws InterruptedException {
        final Solution issue = new Solution();

        Thread t1 = new Thread(issue::readFromLIst);

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                issue.removeItemsFromList();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });

        t2.start();
        t1.start();


        t1.join();
        t2.join();

        System.out.println("Final List: " + issue.list);

    }

}

Выход:

READ Item_1
REMOVE Item_0
READ Item_2
REMOVE Item_1
READ Item_3
REMOVE Item_2
READ Item_4
REMOVE Item_3
READ Item_5
REMOVE Item_4
REMOVE Item_5
READ Item_6
READ Item_7
REMOVE Item_6
READ Item_8
REMOVE Item_7
READ Item_9
REMOVE Item_8
READ Item_10
REMOVE Item_9
READ Item_11
REMOVE Item_10
READ Item_12
REMOVE Item_11
READ Item_13
REMOVE Item_12
READ Item_14
REMOVE Item_13
READ Item_15
REMOVE Item_14
READ Item_16
REMOVE Item_15
READ Item_17
REMOVE Item_16
READ Item_18
REMOVE Item_17
READ Item_19
REMOVE Item_18
READ Item_20
REMOVE Item_19
REMOVE Item_20
READ Item_21
READ Item_22
REMOVE Item_21
REMOVE Item_22
READ Item_23
READ Item_24
REMOVE Item_23
REMOVE Item_24
READ Item_25
REMOVE Item_25
READ Item_26
READ Item_27
REMOVE Item_26
READ Item_28
REMOVE Item_27
READ Item_29
REMOVE Item_28
READ Item_29
REMOVE Item_29
Final List: []
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...