我在Mike Ash的"照顾和喂养单身人士"中遇到了这个问题,他的评论有点困惑:
不过,这段代码有点慢.拿锁是有点贵的.令人痛苦的是,在绝大多数情况下,锁定毫无意义.只有当foo为nil时才需要锁定,这基本上只发生一次.在单例初始化之后,对锁的需求消失了,但锁本身仍然存在.
+(id)sharedFoo {
static Foo *foo = nil;
@synchronized([Foo class]) {
if(!foo) foo = [[self alloc] init];
}
return foo;
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,毫无疑问这是一个很好的理由,但为什么你不能写(见下文)来限制当foo为零时的锁定?
+(id)sharedFoo {
static Foo *foo = nil;
if(!foo) {
@synchronized([Foo class]) {
foo = [[self alloc] init];
}
}
return foo;
}
Run Code Online (Sandbox Code Playgroud)
欢呼加里
我有一个人们要求资源的网络应用程序.为了提高效率,使用同步哈希映射缓存此资源.这里的问题是当同时为同一个未缓存的资源发出两个不同的请求时:检索资源的操作会占用大量内存,所以我想避免为同一个资源多次调用它.
有人可以告诉我,以下代码段是否存在任何潜在问题?提前致谢.
private Map<String, Resource> resources = Collections.synchronizedMap(new HashMap<String, Resource>());
public void request(String name) {
Resource resource = resources.get(name);
if (resource == null) {
synchronized(this) {
if (resources.get(name) == null) {
resource = veryCostlyOperation(name); // This should only be invoked once per resource...
resources.put(resource);
} else {
resource = resources.get(name);
}
}
}
...
}
Run Code Online (Sandbox Code Playgroud) java multithreading synchronization caching double-checked-locking
我有一个多线程应用程序和一个单例类:
public final class Singleton {
private static MyClass mc;
public static final Object getInstance() {
if(mc == null) {
mc = new MyClass();
}
return mc;
}
}
Run Code Online (Sandbox Code Playgroud)
当然,这在一般的多线程场景中不起作用.但请考虑以下情形:
getInstance()第一次调用,以便初始化mc.我的假设:
这应该工作,因为mc字段的初始化和对象的构造发生 - 在Thread.start()启动其他线程的所有后续调用之前.并且Thread.start()线程发生在该线程的所有其他操作之前.它遵循的初始化mc之前发生的所有其他线程的所有行动,以便getInstance()将返回所有线程的权值.
这个假设是对的吗?为什么/为什么不呢?
在某种程度上,这个问题相当简单。假设我有这个类:
static class Singleton {
}
Run Code Online (Sandbox Code Playgroud)
我想为它提供一个单例工厂。我可以做(可能)显而易见的事情。我不打算提及枚举的可能性或任何其他可能性,因为我对它们不感兴趣。
static final class SingletonFactory {
private static volatile Singleton singleton;
public static Singleton getSingleton() {
if (singleton == null) { // volatile read
synchronized (SingletonFactory.class) {
if (singleton == null) { // volatile read
singleton = new Singleton(); // volatile write
}
}
}
return singleton; // volatile read
}
}
Run Code Online (Sandbox Code Playgroud)
我可以volatile read以更高的代码复杂性为代价摆脱一个:
public static Singleton improvedGetSingleton() {
Singleton local = singleton; // volatile read
if (local == null) {
synchronized …Run Code Online (Sandbox Code Playgroud) 我正在阅读文章Double-checked locking和Singleton模式,关于双重检查锁定如何被破坏,以及Stack Overflow上的一些相关问题.
我已经多次使用这种模式/习语而没有任何问题.由于我一直在使用Java 5,我首先想到的是这已经在Java 5内存模型中得到了纠正.然而文章说:
本文引用了Java 5.0之前的Java内存模型; 关于内存排序的陈述可能不再正确.然而,在新的内存模型下,双重检查的锁定习惯仍然被打破.
这是一个真正的问题,如果是这样,在什么条件下?
仅限Java 5及以上版本.假设一个多处理器共享内存计算机(你现在可能正在使用它).
这是一个单例的延迟初始化代码:
public final class MySingleton {
private static MySingleton instance = null;
private MySingleton() { }
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
if (instance == null) {
instance = new MySingleton();
}
}
}
return instance;
}
}
Run Code Online (Sandbox Code Playgroud)
是否instance必须声明volatile,以阻止优化重写的getInstance()如下(这将是一个连续的程序是正确的):
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
// instance must be null or we wouldn't be here (WRONG!)
instance = new MySingleton(); …Run Code Online (Sandbox Code Playgroud) 我是PHP的新手,所以要开始我已经决定实现一个单例.
虽然我能够在php中重新创建单例模式,但我不确定如何实现双重检查锁定.
这在PHP中是否可行/需要.我在某处读过PHP不是多线程的?有人可以证实吗?
如果它是多线程的,有人可以向我解释一下lock()或synchronize()在PHP中是如何工作的吗?
谢谢,亨利
php singleton multithreading synchronize double-checked-locking
在http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html的底部,它说:
双重检查锁定不可变对象
如果Helper是一个不可变对象,使得Helper的所有字段都是最终的,那么双重检查锁定将无需使用volatile字段即可工作.我们的想法是对不可变对象(如String或Integer)的引用应该与int或float的行为方式大致相同; 读取和写入对不可变对象的引用是原子的.
可变的样本和解释如下:
// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null)
synchronized(this) {
if (helper == null)
helper = new Helper();
}
return helper;
}
// other functions and members...
}
Run Code Online (Sandbox Code Playgroud)
它不起作用的第一个原因
最明显的原因是,初始化Helper对象和写入辅助对象字段的写入可以无序完成或感知.因此,调用getHelper()的线程可以看到对辅助对象的非null引用,但是请参阅辅助对象的字段的默认值,而不是构造函数中设置的值.
如果编译器内联对构造函数的调用,那么如果编译器可以证明构造函数不能抛出异常或执行同步,则可以自由地重新排序初始化对象和写入辅助对象字段的写入.
即使编译器没有重新排序这些写入,在多处理器上,处理器或内存系统也可能重新排序这些写入,正如在另一个处理器上运行的线程所感知的那样.
我的问题是:为什么不可变类没有问题?我看不出重新排序与该类是否可变的任何关系.
谢谢
Meyers的书" Effective Modern C++ "第16项中的一个例子.
在一个缓存昂贵的计算int的类中,您可能会尝试使用一对std :: atomic avriable而不是互斥锁:
class Widget {
public:
int magicValue() const {
if (cachedValid) {
return cachedValue;
} else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = va1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid { false };
mutable std::atomic<int> cachedValue;
};
Run Code Online (Sandbox Code Playgroud)
这样可以工作,但有时它会比它应该工作得多.考虑:一个线程调用Widget :: magicValue,将cacheValid视为false,执行两个昂贵的计算,并将它们的总和分配给cachedValud.此时,第二个线程calidget Widget :: magicValue也将cacheValid视为false,因此执行与第一个线程刚刚完成的相同的昂贵计算.
然后他用互斥量给出了一个解决方案:
class Widget {
public:
int magicValue() const {
std::lock_guard<std::mutex> guard(m);
if (cacheValid) {
return cachedValue;
} else …Run Code Online (Sandbox Code Playgroud) 回到并发.到目前为止,很明显,为了double checked locking工作,需要将变量声明为volatile.但是,如果使用双重检查锁定,如下所示.
class Test<A, B> {
private final Map<A, B> map = new HashMap<>();
public B fetch(A key, Function<A, B> loader) {
B value = map.get(key);
if (value == null) {
synchronized (this) {
value = map.get(key);
if (value == null) {
value = loader.apply(key);
map.put(key, value);
}
}
}
return value;
}
}
Run Code Online (Sandbox Code Playgroud)
为什么它必须是ConcurrentHashMap而不是常规的HashMap?所有映射修改都在synchronized块内完成,代码不使用迭代器,因此从技术上讲,应该没有"并发修改"问题.
请避免建议使用putIfAbsent/,computeIfAbsent因为我询问的概念而不是API的使用:)除非使用此API有助于HashMapvs ConcurrentHashMapsubject.
更新2016-12-30
这个问题由Holger下面的评论回答" HashMap.get …
java concurrency hashmap concurrenthashmap double-checked-locking
java ×7
singleton ×3
concurrency ×2
volatile ×2
atomic ×1
c++ ×1
caching ×1
cocoa ×1
hashmap ×1
immutability ×1
mutex ×1
objective-c ×1
php ×1
synchronize ×1
synchronized ×1