如何证明类 ListHelper<E> 不是线程安全的

最近在看《Java Concurrency in Practice 2nd》一书,作者提到如果我们使用Collections.synchronizedList来创建一个安全线程List,那么我们必须确保我们使用的是同一个锁,它是来自SynchronizedCollection的一个Object。下面的代码来自书中:


public class ListHelper <E> {

    public List<E> list = Collections.synchronizedList(new ArrayList<E>());


    public synchronized boolean putIfAbsent(E x) {

        boolean absent = !list.contains(x);

        if (absent)

            list.add(x);

        return absent;

    }

}

在这个类中,方法putIfAbsent已经被ListHelper中的一个Object锁住了,但是list.contains并没有使用这个Object作为锁,有两个锁,所以在多线程下是不安全的。但我的问题是如何证明它不是线程安全的。你有什么想法?


湖上湖
浏览 128回答 2
2回答

隔江千里

以下代码证明您的类不是线程安全的。它在两个不同的线程中将 100000 个数字添加到列表中:t1使用putIfAbsent你的类的方法t2使用synchronized块正确锁定synchronizedList用于控制访问的相同“互斥”对象,即包装器列表本身。由于两种方法都试图添加相同的100000 个对象,结果应该是 100000 个对象的列表,即代码应该100000在最后打印。有时它确实如此,当我运行它时,但大多数时候它比那个高一点,例如100075,从而证明您putIfAbsent的不是线程安全的。import java.util.ArrayList;import java.util.Collections;import java.util.List;public class Test {&nbsp; &nbsp; public static void main(String[] args) throws Exception {&nbsp; &nbsp; &nbsp; &nbsp; ListHelper<Integer> helper = new ListHelper<>();&nbsp; &nbsp; &nbsp; &nbsp; Thread t1 = new Thread(() -> Test.t1(helper));&nbsp; &nbsp; &nbsp; &nbsp; Thread t2 = new Thread(() -> Test.t2(helper));&nbsp; &nbsp; &nbsp; &nbsp; t1.start();&nbsp; &nbsp; &nbsp; &nbsp; t2.start();&nbsp; &nbsp; &nbsp; &nbsp; t1.join();&nbsp; &nbsp; &nbsp; &nbsp; t2.join();&nbsp; &nbsp; &nbsp; &nbsp; System.out.println(helper.list.size());&nbsp; &nbsp; }&nbsp; &nbsp; private static void t1(ListHelper<Integer> helper) {&nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i < 100000; i++)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; helper.putIfAbsent(i);&nbsp; &nbsp; }&nbsp; &nbsp; private static void t2(ListHelper<Integer> helper) {&nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i < 100000; i++)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; synchronized (helper.list) { // correct way to synchronize&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (! helper.list.contains(i))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; helper.list.add(i);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}class ListHelper <E> {&nbsp; &nbsp; public List<E> list = Collections.synchronizedList(new ArrayList<E>());&nbsp; &nbsp; public synchronized boolean putIfAbsent(E x) {&nbsp; &nbsp; &nbsp; &nbsp; boolean absent = ! list.contains(x);&nbsp; &nbsp; &nbsp; &nbsp; if (absent)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; list.add(x);&nbsp; &nbsp; &nbsp; &nbsp; return absent;&nbsp; &nbsp; }}

Cats萌萌

通过确保在操作之间运行第二个线程,您可以表明您添加的第一个元素可能存在问题。import java.util.ArrayList;import java.util.Collections;import java.util.List;public class ListHelper<E> {&nbsp; &nbsp; public final List<E> list = Collections.synchronizedList(new ArrayList<E>());&nbsp; &nbsp; public synchronized boolean putIfAbsent(E x) {&nbsp; &nbsp; &nbsp; &nbsp; boolean absent = !list.contains(x);&nbsp; &nbsp; &nbsp; &nbsp; runInANotherThread(() -> list.add(x));&nbsp; &nbsp; &nbsp; &nbsp; if (absent)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; list.add(x);&nbsp; &nbsp; &nbsp; &nbsp; return absent;&nbsp; &nbsp; }&nbsp; &nbsp; public static void runInANotherThread(Runnable run) {&nbsp; &nbsp; &nbsp; &nbsp; Thread t = new Thread(run);&nbsp; &nbsp; &nbsp; &nbsp; t.start();&nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.join(1000);&nbsp; &nbsp; &nbsp; &nbsp; } catch (InterruptedException ignored) {&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; public static void main(String[] args) {&nbsp; &nbsp; &nbsp; &nbsp; ListHelper<Integer> list = new ListHelper<>();&nbsp; &nbsp; &nbsp; &nbsp; list.putIfAbsent(1);&nbsp; &nbsp; &nbsp; &nbsp; System.out.println(list.list);&nbsp; &nbsp; }}印刷[1, 1]
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java