Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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_Concurrency_Java.util.concurrent_Double Checked Locking - Fatal编程技术网

将双重检查锁定从使用同步转换为JAVA中的锁定

将双重检查锁定从使用同步转换为JAVA中的锁定,java,multithreading,concurrency,java.util.concurrent,double-checked-locking,Java,Multithreading,Concurrency,Java.util.concurrent,Double Checked Locking,考虑以下代码在JAVA 8中使用synchronized关键字实现双重检查锁定: private static void redoHeavyInitialisation() { if (needToReinitialise()) { synchronized (MyClass.class) { if (needToReinitialise()) { doHeavyInitialisation();

考虑以下代码在JAVA 8中使用
synchronized
关键字实现双重检查锁定:

private static void redoHeavyInitialisation() {
    if (needToReinitialise()) {
        synchronized (MyClass.class) {
            if (needToReinitialise()) {
                doHeavyInitialisation();
            }
        }
    }
}
之所以使用双重检查锁定,是因为初始化很重(因此很懒),并且可能会发生多次(因此不能使用单例模式,如果我错了,请纠正我)

无论如何,首先,如何将上面的代码转换为使用JAVA并发包中的
Lock
,而不是使用synchronized关键字

只有在这之后,或者可以选择使用Lock或synchronized关键字评论哪一个更好


记住,这个问题不是关于锁与同步的比较。未回答代码转换部分的回答尝试将不会被选为接受回答。

考虑以下代码:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HeavyInitializer {
static final Logger logger = LoggerFactory.getLogger(HeavyInitializer.class);
static HeavyInitializer singleton;
public static synchronized HeavyInitializer getInstance() {
    if (singleton==null) {
        singleton = new HeavyInitializer();
    }
    return singleton;
}
boolean initialized;
private HeavyInitializer() {
    initialized = false;
}

public synchronized void initialize() {
     if (!initialized) {
         heavyStuffDoneHere();
     }
}
public synchronized void reInitilize() {
    if (needToReinitialise()) {
        heavyStuffDoneHere();
    }
}

private void heavyStuffDoneHere() {
    initialized = true;
}

private boolean needToReinitialise() {
    if (!initialized)
       return false;
    boolean ret = false;
    //Do your check here... and set ret     
    return ret;
}

}
发件人:

。。。那么使这些方法同步有两个效果:

  • 首先,同一对象上的两个同步方法调用不可能交错。当一个线程为一个对象执行同步方法时,调用同一对象块的同步方法的所有其他线程(暂停执行),直到第一个线程对该对象执行完毕

  • 其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立“发生在之前”关系。这保证了对象状态的更改对所有线程都可见


尝试使用Lock就是尝试重新实现同步块。没有必要

使用
ReentrantLock
将同步块转换为等效块相当死记硬背

首先,创建一个与锁定对象具有相同或类似作用域和生存期的锁。这里您锁定的是
MyClass.class
,因此是一个静态锁,因此您可以将其映射到
MyClass
中的静态锁,例如
MyClass.initLock

然后,只需更换每个:

synchronized (object) {

以及每个关联的右括号

} finally {
  lock.unlock();
}
总而言之,您拥有:

private final static ReentrantLock initLock = new ReentrantLock();

private static void redoHeavyInitialisation() {
    if (needToReinitialise()) {
        MyClass.initLock.lock();
        try {
            if (needToReinitialise()) {
                doHeavyInitialisation();
            }
        } finally {
          MyClass.initLock.unlock();
        }
    }
}

就性能而言,两种方法之间几乎没有阳光。它们本质上具有相同的语义,并且通常使用相似的底层机制。在过去,存在性能差异——有时优化会影响其中一个,因此在某些JVM上可以找到差异,但双重检查锁定的全部目的是避免使用锁,所以只需执行最简单的操作即可。当
needtoreainitialise()
方法运行时,您只能在很短的一段时间内获得锁,因此锁定成本不会产生任何持续影响。

Singleton重复检查锁,并使用序列化防止Singleton对象中断

包模式.core.java; 导入java.io.Serializable

公共类Singleton扩展对象实现可序列化{

private static final long serialVersionUID = 1L;
private static Singleton sg;

private Singleton() {
}

public static Singleton getSingletonObj() {
    if (sg == null) {
        synchronized (sg) {
            if (sg == null) {
                sg = new Singleton();
            }
        }
    } 
    return sg;
}


/*
 * this method ensures that new object will not be created for singleton
 * class using serialization and deserialization
 */
protected Object readResolve() {
    return sg;
}

/*
 * @Override protected Object clone() throws CloneNotSupportedException {
 * throw new CloneNotSupportedException(); }
 */

@Override
protected Object clone() throws CloneNotSupportedException {
    return sg;
}

}

如果您只关注比较,而完全忽略了要求等效代码转换(这是主要问题)的问题,则@ErwinBolwidt的可能重复是。如果你不想,你不必对比较进行评论,但是请回答代码转换。你可以使用singleton。一种可能的架构是创建一个Singleton HeavyInitializer类,该类使用同步方法初始化和重新初始化。@Chocksmith我不知道使用Singleton进行重新初始化的方法,因为我知道的所有Singleton模式都依赖于Singleton实例的非并发初始化。在创建了第一个也是唯一一个实例之后,您如何重新触发该事件?您不应该与那些试图帮助您的人交谈,就好像他们正在接替通常为您做作业的仆人一样。谢谢。几乎是最好的答案。您能解释一下为什么它必须是
ReentrantLock
,而不是使用
Lock
的其他变体吗?
ReentrantLock
是我所知道的Java 8中的
Lock
接口的唯一直接实例化实现。你在谈论什么其他的实现@用户1589188对不起,我指的是锁包。其他人喜欢读/写、盖章、锁支持。不知道你为什么要在这里使用这些
ReentrantLock
synchronized
的直接替代品,适合您的使用。其他的完全是其他的东西,在“锁支持”的情况下,甚至没有锁@user1589188
ReentrantLock
直接替代了synchronized fair Tour。有了这条补充信息,一切都很好。谢谢你的努力。我很想给你打分,但是你的问题使用了
synchronized
,就像我问题中的例子一样,它根本没有回答我的问题。只是尝试贡献和展示一个替代体系结构。我的经验表明,重新实现与锁或信号量同步将导致执行时难以解决的bug。我将使用同步的单例体系结构。
private static final long serialVersionUID = 1L;
private static Singleton sg;

private Singleton() {
}

public static Singleton getSingletonObj() {
    if (sg == null) {
        synchronized (sg) {
            if (sg == null) {
                sg = new Singleton();
            }
        }
    } 
    return sg;
}


/*
 * this method ensures that new object will not be created for singleton
 * class using serialization and deserialization
 */
protected Object readResolve() {
    return sg;
}

/*
 * @Override protected Object clone() throws CloneNotSupportedException {
 * throw new CloneNotSupportedException(); }
 */

@Override
protected Object clone() throws CloneNotSupportedException {
    return sg;
}