了解Java

时间:2019-05-25 12:16:29

标签: java multithreading synchronized java-threads

我有一个ArrayList,该数组由一个线程修改,并由另一个线程读取。 读取线程很高兴读取修改后列表中剩下的所有内容。从列表中删除所有项目后,它应该停止。 在Collections.synchronizedList()的Java文档中提到,在对列表执行任何操作之前,我们应该手动对其进行同步。

  

用户必须手动对返回的内容进行同步   遍历时列出:

     

列表列表= Collections.synchronizedList(new ArrayList());         ...已同步(列表){         迭代器i = list.iterator(); //必须在同步块中         而(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读取: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   删除: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 (没有 synchronized 块)来工作,但是每次修改列表时创建新列表似乎都有开销。

我在使用sychronizedList获得与CopyOnWriteArrayList相同的结果时是否缺少某些东西?

1 个答案:

答案 0 :(得分:1)

对于synchronizedList,您必须在遍历时按住该锁。

但是,由于您仅在列表上执行删除优先和遍历,因此可以考虑使用ConcurrentLinkedDeque。来自ConcurrentLinkedDeque的文档:

  

迭代器是弱一致性的,返回的元素在创建迭代器之时或之后反映出双端队列的状态。它们不会引发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: []