考虑"对实例字段的延迟初始化进行双重检查":
// Item 71 in Effective Java copied from this interview with Bloch. private volatile FieldType field; FieldType getField() { FieldType result = field; if (result == null) { // First check (no locking) synchronized(this) { result = field; if (result == null) // Second check (with locking) field = result = computeFieldValue(); } } return result; }
我希望能够以安全的方式重置字段(强制它再次从数据库加载,在我的情况下).我假设我们可以通过重置方法来做到这一点:
void reset() {
field = null;
}
这是重置场地的标准方法吗?安全吗?任何陷阱?我问,因为布洛赫发出了关于双重检查懒惰加载的以下警告:"成语非常快,但也很复杂和细腻,所以不要试图以任何方式修改它.只需复制和粘贴 - 通常这不是一个好主意,但在这里是合适的."
在此先感谢喜马拉雅山脉的Playa.
Checkstyle将此代码报告为"双重检查锁定习语已损坏",但我认为我的代码实际上并未受到双重检查锁定问题的影响.
如果不存在具有该id的行,则该代码应该在数据库中创建一行.它在多线程环境中运行,我想避免主键存在的SQL异常.
伪代码:
private void createRow(int id) {
Row row = dao().fetch(id);
if (row == null) {
synchronized (TestClass.class) {
row = dao().fetch(id);
if (row == null) {
dao().create(id);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我可以同意它看起来像双重检查锁定,但我没有使用静态变量,fetch()和create()中的代码可能太复杂,无法内联并使其无序.
我错了还是格式?:)
所以我看到很多文章现在声称在C++上双重检查锁定,通常用于防止多个线程尝试初始化一个懒惰的单例,被打破了.正常双重检查锁定代码如下所示:
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
public:
static singleton & instance()
{
static singleton* instance;
if(!instance)
{
boost::mutex::scoped_lock lock(_init_mutex);
if(!instance)
instance = new singleton;
}
return *instance;
}
};
Run Code Online (Sandbox Code Playgroud)
问题显然是行分配实例 - 编译器可以自由分配对象,然后将指针分配给它,或者将指针设置为它将被分配的位置,然后分配它.后一种情况打破了这个习惯用法 - 一个线程可以分配内存并分配指针,但在它进入休眠状态之前不运行单例的构造函数 - 然后第二个线程将看到该实例不为null并尝试返回它,即使它尚未建成.
我看到了一个使用线程局部布尔值的建议并检查而不是instance.像这样的东西:
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
static boost::thread_specific_ptr<int> _sync_check;
public:
static singleton & instance()
{
static …Run Code Online (Sandbox Code Playgroud) singleton multithreading locking sequence-points double-checked-locking
由于内存模型,我意识到双重检查锁定在java中存在缺陷,但这通常与单例模式相关联并优化单例的创建.
在这个案例中,在objective-c中怎么样:
我有一个布尔标志来确定我的应用程序是否是流数据.我有3个方法,startStreaming,stopStreaming,streamingDataReceived,我使用以下方法保护它们免受多个线程的影响:
- (void) streamingDataReceived:(StreamingData *)streamingData {
if (self.isStreaming) {
@synchronized(self) {
if (self.isStreaming) {
- (void) stopStreaming {
if (self.isStreaming) {
@synchronized(self) {
if (self.isStreaming) {
- (void) startStreaming:(NSArray *)watchlistInstrumentData {
if (!self.isStreaming) {
@synchronized(self) {
if (!self.isStreaming) {
Run Code Online (Sandbox Code Playgroud)
这次双重检查不成功吗?双重检查在objective-c中是否与java中有类似的问题?这种模式有哪些替代方案(反模式).
谢谢
在C#中,以下代码(来自此页面)可用于以线程安全的方式懒惰地实例化单例类:
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
lock(this) {
if (helper == null)
helper = new Helper();
}
}
return helper;
}
}
Run Code Online (Sandbox Code Playgroud)
什么是等效的线程安全Delphi代码?
文章还提到了Java中双重检查锁定的两个问题:
因此,虽然上述文章中的C#代码和Java版本看起来几乎相同,但只有C#版本按预期工作.如果在Delphi版本的Double-Checked Locking中也存在这两个问题,那么会引出另外一个问题?
delphi singleton design-patterns thread-safety double-checked-locking
此代码是否解决了Java中的双重检查锁定问题?
public class DBAccessService() {
private static DBAccessService INSTANCE;
private DBAccessService() {}
public static DBAccessService getInstance() {
if (INSTANCE != null) {
return INSTANCE;
}
return createInstance();
}
private static synchronized DBAccessService createInstance() {
if (INSTANCE != null) {
return INSTANCE;
}
DBAccessService instance = new DBAccessService();
INSTANCE = instance;
return INSTANCE;
}
}
Run Code Online (Sandbox Code Playgroud)
有两个方面需要注意:
getInstance()是不同步的,所以INSTANCE被初始化之后没有用于同步没有成本createInstance() 是同步的所以,问题是:这段代码有什么问题吗?它合法且始终是线程安全的吗?
我继续运行使用双重检查锁定的代码,我仍然对它为什么会被使用感到困惑.
我最初不知道双重检查锁定是否被打破,当我学会它时,它为我放大了这个问题:为什么人们首先使用它?是不是比较和交换更好?
if (field == null)
Interlocked.CompareExchange(ref field, newValue, null);
return field;
Run Code Online (Sandbox Code Playgroud)
(我的问题同时适用于C#和Java,尽管上面的代码是针对C#的.)
与原子操作相比,双重检查锁定是否具有某种固有优势?
c# java synchronization compare-and-swap double-checked-locking
最近我一直在重构我的一些C#代码,我发现了一些双重检查锁定实践.那时我不知道这是一个不好的做法,我真的想摆脱它.
问题是我有一个应该被懒惰地初始化并且经常被许多线程访问的类.我也不想将初始化移动到静态初始化器,因为我计划使用弱引用来保持初始化对象在内存中保持太长时间.但是,如果需要,我想"恢复"对象,确保以线程安全的方式发生这种情况.
我想知道是否在C#中使用ReaderWriterLockSlim并在第一次检查之前输入UpgradeableReadLock,然后如果需要,为初始化输入写锁定将是可接受的解决方案.以下是我的想法:
public class LazyInitialized
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private volatile WeakReference _valueReference = new WeakReference(null);
public MyType Value
{
get
{
MyType value = _valueReference.Target as MyType;
_lock.EnterUpgradeableReadLock();
try
{
if (!_valueReference.IsAlive) // needs initializing
{
_lock.EnterWriteLock();
try
{
if (!_valueReference.IsAlive) // check again
{
// prevent reading the old weak reference
Thread.MemoryBarrier();
_valueReference = new WeakReference(value = InitializeMyType());
}
}
finally
{
_lock.ExitWriteLock();
}
}
}
finally
{
_lock.ExitUpgradeableReadLock();
}
return value;
} …Run Code Online (Sandbox Code Playgroud) 在关于双重检查锁定习语的文章中,我发现了这个引用:
延迟初始化的一个特殊情况是在没有同步的情况下按预期工作的是静态单例.当初始化对象是没有其他方法或字段的类的静态字段时,JVM会自动有效地执行延迟初始化.
为什么强调部分很重要?为什么如果没有它的工作是其他方法或领域?
(这篇文章已经超过10年了.信息是否仍然相关?)
java singleton jvm double-checked-locking lazy-initialization
在Java程序员中众所周知,为了使双重检查锁定正常运行,必须声明变量volatile,并且同步对象的初始化是不够的.
意识可能主要是因为volatile关键字的语义在1.5中改变为包括"之前发生"关系,至少部分是为了使双重检查锁定安全; 根据我的理解,"发生在之前"关系意味着写入volatile变量会导致线程中的所有缓存变量被写入主内存,并且在从volatile变量读取后,所有缓存变量都被认为是陈旧的,并且必须是从主内存重新读取,以便在写入volatile变量之前写入的所有内容都保证在"稍后从该变量读取之前"发生.
Stack Overflow 似乎认为,对于C#来说,volatile双重检查锁定是不必要的(尽管有人担心这可能是特定于某些CPU或微软的实现),同时也认为Java synchronized语句的语义与 C#的语义完全相同lock声明,这表明在C#中也存在Java中发现的相同问题,除非在两种语言之间的双重检查锁定的语义中存在一些其他主要差异.
那么......哪个是正确的?C#中的双重检查锁定实际上比Java更危险吗?如果是这样,那么语言语义有何不同呢?
如果不是,没有具体可能出错的是volatile什么?volatileC#中的语义是否像Java一样建立了"发生在之前"的关系,因此双重检查锁定在C#中是安全的,volatile因为它在Java中是1.5?