有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java在同一个锁上同步

我正在读的一本书的摘录-

//在错误的锁上同步,因此@NotThreadSafe

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;
    }
}

//在相同/正确的锁上同步,因此@ThreadSafe
//如果不存在,则通过客户端锁定实现Put

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;
        }
    }
}

接着,作者迅速转到第三个案例-
//如果没有,使用组合实现put

public class ImprovedList<E> implements List<E> {

    private final List<E> list;

    public ImprovedList(List<E>  list){
        this.list = list;
    }

    public synchronized boolean putIfAbsent(E x){

        boolean contains = list.contains(x);
        if(contains)
            list.add(x);
        return !contains;

    }
}

上面的类@ThreadSafe是怎样的,即使public final List<E> list;中的list可能not be @ThreadSafe


共 (2) 个答案

  1. # 1 楼答案

    我在一本书中也看到了这一点,我的代码如下

    public class ListHelper {
    
     public List<String> list =  Collections.synchronizedList(new ArrayList<String>());
    
        public synchronized boolean putIfAbsent(String x) throws InterruptedException{// lock is ListHelper
            boolean absent = !list.contains(x);
            Thread.sleep(1000);
            if(absent)
                list.add(x);
            return absent;
        }
    
       public void addList(String str) throws InterruptedException{
           Thread.sleep(500);
           list.add(str); // lock is SynchronizedCollection mutex 
       }
    
    public static void main(String[] args) throws InterruptedException {
        ListHelper lh = new ListHelper();
        MyThread t1 = new MyThread(lh);
        MyThread2 t2 = new MyThread2(lh);
        t1.start();
        t2.start();
        Thread.sleep(1500);
        System.out.println("size="+lh.list.size());
        for(String str : lh.list){
            System.out.println("item="+str);
        }
    
    }
    

    }

    class MyThread extends Thread{
    ListHelper lh;
    public MyThread(ListHelper lh){
        this.lh=lh;
    }
    
    @Override
    public void run() {
        try {
            lh.putIfAbsent("maksim");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    

    }

    class MyThread2 extends Thread{
    ListHelper lh;
    public MyThread2(ListHelper lh){
        this.lh=lh;
    }
    @Override
    public void run() {
        try {
            lh.addList("maksim");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    
    }
    

    }

  2. # 2 楼答案

    Can you tell me why in the second case, there is synchronized(list){...} even when list itself is synchronised.

    如果你完全用其他线程安全的对象来创建一个对象O,那并不一定意味着O是线程安全的

    如果取出synchronized语句,然后让两个线程同时使用相同的x调用listHelper.putIfAbsent(x)方法,会怎么样?两个线程都可以同时调用list.contains(x),并且都可以将absent设置为true。一旦到达该点,它们都将调用list.add(x),线程安全列表将包含对同一对象的两个引用

    这似乎不是你想要的

    list的线程安全意味着,当两个线程同时调用list.add(x)时,它们不会对列表的内部结构造成损害。这意味着list将履行其承诺,即使多个线程同时使用它

    list.contains(x)方法实现了它的承诺:它两次返回false,因为x不在列表中。这个list.add(x)实现了它的承诺,它两次都在列表中加入了一个x引用,就像它被告知的那样

    问题(如果取消同步)不在list对象的任何地方,因为列表只包含一个x引用的要求是您的要求

    如果不希望列表包含多个x引用,那么必须编写代码来防止它发生。这就是synchronized语句的作用。它通过测试x是否在列表中,然后对它做一些事情来进行原子操作。“原子”基本上意味着,两个线程不能同时执行


    The class in the second case is thread safe as given in JCIP

    “线程安全”一词没有确切的含义。我没有说ListHelper线程安全的;我说把它叫做“线程安全”是不对的

    我只是采取了比布洛赫先生更保守的立场,仅此而已

    如果你按照作者希望你使用它的方式使用它,那么这个类是线程安全的。但是通过使list成员public,他也使您以非线程安全的方式使用他的类变得非常容易

    我以写软件为生,在我的工作中,它既不是必要的,也不足以让程序工作。我的工作是制作让顾客满意的节目。如果有一种错误的方式来使用我的程序,如果我让错误的方式比正确的方式更吸引人,那么客户不会高兴,我的老板也不会高兴,而借口“是的,但它有效”“也不会让我摆脱困境