Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/345.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在同一锁上同步_Java_Multithreading_Synchronization - Fatal编程技术网

Java 在同一锁上同步

Java 在同一锁上同步,java,multithreading,synchronization,Java,Multithreading,Synchronization,我正在读的一本书的摘录- //在错误的锁上同步,因此@NotThreadSafe public class ListHelper<E> { public List<E> list = Collections.synchronizedList(new ArrayList<E>()); public synchronized boolean putIfAbsent(E x){ boolean absent = !list.co

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

//在错误的锁上同步,因此@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;
    }
}
公共类ListHelper{
public List=Collections.synchronizedList(新的ArrayList());
公共同步布尔putIfAbsent(E x){
布尔缺席=!list.contains(x);
如果(缺席)
增加(x);
缺席返回;
}
}
//在相同/正确的锁上同步,因此@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;
        }
    }
}
公共类ListHelper{
public List=Collections.synchronizedList(新的ArrayList());
公共布尔putIfAbsent(E x){
已同步(列表){
布尔缺席=!list.contains(x);
如果(缺席)
增加(x);
缺席返回;
}
}
}
快速前进,作者转到第三个案例-
//使用组合实现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;

    }
}
public类ImprovedList实现列表{
私人最终名单;
公共改进列表(列表){
this.list=列表;
}
公共同步布尔putIfAbsent(E x){
布尔包含=list.contains(x);
如果(包含)
增加(x);
返回!包含;
}
}

即使
公共最终列表中的
列表
,上述类的线程安全性如何
可能不是@ThreadSafe

我相信这个例子来自JCIP。鉴于此,

在案例3中-它仍然是线程安全的,因为您通过
公共同步布尔putIfAbsent(ex)
获得了对
改进列表
对象的锁定。这里需要注意的关键点是即使这个类中所有其他使用
list
的方法都使用
synchronized
关键字。因此,在任何给定时间,只有一个线程能够使用这个
list
对象

如果我们尝试添加其他与列表相关的方法,并了解其工作原理,就会更加清楚

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

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

    public synchronized void add(E x){
        list.add(x);
    }
    //...Other list methods, but using synchronized keyword as above method.
}
因此,基本上总结是使用与list相同的锁(case-2)或使用显式锁(case-3)

关于@Markspace问题:


该列表是公开的,不是线程安全的。这是什么书

这是真的,在这方面,书中增加了一个警告。上面说,

如Collections.synchronizedList和其他集合 wrappers,ImprovedList假设一旦列表被传递给它的构造函数,客户端将不再直接使用基础列表,只通过ImprovedList访问它


你能告诉我为什么在第二种情况下,即使列表本身是同步的,也会有synchronized(list){…}

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

public class ListHelper<E> {
    private final List<E> list = new ArrayList<E>();
    public boolean putIfAbsent(E x) {
        synchronized (list) {
            boolean absent = !list.contains(x);
            if (absent) {
                list.add(x);
            }
            return absent;
        }
    }
}
如果取出
synchronized
语句,然后让两个线程同时使用相同的
x
调用
listHelper.putIfAbsent(x)
方法,会怎么样?两个线程都可以同时调用
list.contains(x)
,并且都可以将
缺席设置为true。一旦到达该点,它们都将调用
list.add(x)
,线程安全列表将包含对同一对象的两个引用

这似乎不是你想要的

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

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

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

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


第二种情况下的类是线程安全的,如JCIP中所示

“线程安全”一词没有确切的含义。我没有说
ListHelper
不是线程安全的;我说过称之为“线程安全”是错误的

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

如果您按照作者希望您使用的方式使用该类,则该类是线程安全的。但是,通过将
列表
成员
公开
,他还使您可以非常轻松地以非线程安全的方式使用他的类


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

简短回答。你需要再读一本书。这一条既令人困惑又不正确

//在错误的锁上同步,因此@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;
    }
}
我假设书中说这是在错误对象上进行的
同步
(不在“锁”上同步)的原因是因为
列表
是一个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();
    }

}