java并发——客户端加锁机制,内置锁——面试题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013276277/article/details/81381990

   看一下下面这个类,他是不是线程安全的?一眼看过去,没毛病啊,肯定安全啊。list是安全的,ListHelp中的方法是安全的,所以这个类是线程安全的。其实,这边是给你制造了一个假象,这个类并不是线程安全的,原因就是方法1的同步方法中对list进行先检查后执行的操作(一般都要求先检查后执行的操作是原子性的),但是list对象并不是你这个方法所私有的,看着list是public的,也就是说,list是发布出去的,其他地方可以修改这个list。而在方法1中执行完list.contain(x)后,其他方法可以对list进行添加或者删除元素操作,导致方法1中的absent结果会失效,当你根据失效的absent结果去进行操作时,必然导致数据上不一致的问题。那么为什么这个类是不安全的呢,因为方法1获得了ListHelp的锁并没有获得list的锁,导致,方法1中对list的先检查后执行操作不是满足原子性的。那么后面的代码即是最终完成该类是线程安全类的答案。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ListHelp<E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());
    
    // 方法1
    public synchronized boolean putIfAbsent(E x) { // 这个方法并不是线程安全的,因为当前只获得了ListHelp的锁,并没有获得list的锁,没有办法保证当前操作的原子性
        boolean absent = !list.contains(x);
        if (absent) {
            list.add(x);
        }
        return absent;
    }
}

线程安全类:方法2直接锁住了ListHelp和list。所以整个先检查后执行操作是原子性的。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ListHelp<E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());
    // 方法2
    public synchronized boolean putIfAbsentSafe(E x) { // 这个方法是线程安全的
        synchronized(list ) {
            boolean absent = !list.contains(x);
            if (absent) {
                list.add(x);
            }
            return absent;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/u013276277/article/details/81381990