关于Java中扩展线程安全类的问题

发布于 2022-09-07 08:18:41 字数 947 浏览 10 评论 0

最近在读《Java并发编程实战》,里面的4.4.1节,有个例子:假设我们需要一个线程安全的List,它需要提供给我们一个原子的“缺少即加入(put-if-absent)”操作。并提供了2种实现:

  1. 非线程安全的“缺少即加入”实现
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;
    }
}
  1. 使用客户端加锁实现的“缺少即加入”
public class ListHelper<E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public boolean putIfAbsent(E x) {
        synchronized (list) {
            boolean absent = !list.contains(x);
            if (absent) {
                list.add(x);
            }
            return absent;
        }
    }
}

感觉书中的翻译比较晦涩,一直没理解这里为什么第一个是非线程安全的而第二个就是线程安全的,请大神看看是怎么回事

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

粉红×色少女 2022-09-14 08:18:41

大概是由于public List<E> list = Collections.synchronizedList(new ArrayList<E>());的这个list是public的,可以被直接修改的。
假如有多个线程直接修改这个list。
方法1虽然把putIfAbsent(E x)这个方法加锁了,但是其他线程依然可以修改由于public暴露出来的list。
比如线程A:listHelper.list.add(x)的时候,线程B刚好在执行boolean absent = !list.contains(x);,有可能线程A把x添加进去了,但是线程B的absent也判断为true,然后线程B就再次add了x。
方法2是根据list加锁,所以只有持有list的锁才能进入判断和添加,当线程A在listHelper.list.add(x)的时候,线程B是不能进入到synchronized (list)方法内的

痞味浪人 2022-09-14 08:18:41

首先要明白,synchronize加锁的一般都是对某个对象而言的(也可以对类进行加锁)。
1中非线程安全的synchronized的加锁对象其实是ListHelper<E>实例化的对象,而不是list,其他的线程无法再对该ListHelper<E>实例化的对象进行操作,对于该例,就是无法再进行putIfAbsent()方法的使用,但是其中的list是public的,所以可以直接对list进行操作,比如list.add()等操作,进而造成线程不安全

栖迟 2022-09-14 08:18:41
Collections.synchronizedList(new ArrayList<E>());方法内部锁住的对象是SynchronizedRandomAccessList

图片描述

图片描述

方法一synchronized修饰方法锁住的是当前对象
方法二list返回的就是SynchronizedRandomAccessList和方法内部的mutex一致

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文