我已经实现了我认为在类中进行双重检查锁定以实现线程安全的延迟加载.
万一你想知道,这是我目前正在研究的DI库.
我正在谈论的代码如下:
final class Builder<I> {
private let body: () -> I
private var instance: I?
private let instanceLocker = NSLock()
private var isSet = false
private let isSetDispatchQueue = DispatchQueue(label: "\(Builder.self)", attributes: .concurrent)
init(body: @escaping () -> I) {
self.body = body
}
private var syncIsSet: Bool {
set {
isSetDispatchQueue.async(flags: .barrier) {
self.isSet = newValue
}
}
get {
var isSet = false
isSetDispatchQueue.sync {
isSet = self.isSet
}
return isSet
}
} …Run Code Online (Sandbox Code Playgroud) multithreading lazy-loading double-checked-locking swift swift4
我看到了这样的答案,试图通过评论来澄清,但对这里的例子不满意。
也许是时候提出这个具体问题了......
为什么枚举单例实现被称为惰性?
public enum EnumLazySingleton {
INSTANCE;
EnumLazySingleton() {
System.out.println("constructing: " + this);
}
public static void touchClass() {}
}
Run Code Online (Sandbox Code Playgroud)
它与急切的实现有何不同?
public class BasicEagerSingleton {
private static final BasicEagerSingleton instance = new BasicEagerSingleton();
public static BasicEagerSingleton getInstance() {
return instance;
}
private BasicEagerSingleton() {
System.out.println("constructing: " + this);
}
public static void touchClass() {}
}
Run Code Online (Sandbox Code Playgroud)
两者都将初始化实例而不访问INSTANCE/getInstance()- 例如 call touchClass()。
public class TestSingleton {
public static void main(String... args) …Run Code Online (Sandbox Code Playgroud) java singleton enums double-checked-locking lazy-initialization
我正在使用java大约一个月,而且我一般都是编程的业余爱好者,所以如果我出错了,请随时纠正我.也许我会提供一些过多的细节,但我现在很困惑,我无法决定什么是重要的.
所以,我一直在开发多线程客户端 - 服务器应用程序.所有线程都使用相同的对象,其中存储了某些配置值和共享记录器; 此对象在服务器线程中初始化,然后作为参数传递给客户端线程类构造函数.首先,假设当服务器启动时,该对象的字段只更改一次,因此并发访问无需担心,但现在需要在修改时从配置文件中重新读取某些配置值,而不必重启服务器.
在一些研究之后想到的第一个想法是创建一个同步方法,当请求类中的某些值时将调用该方法,并且如果我们的配置文件自上次访问后发生更改则会重新读取这些值,否则会立即返回,如这个:
<This code is inside "config" class, instance of which is shared between threads>
private static long lastModified;
private static File configFile;
public class ChangingVariableSet
{
<changing variables go here>
}
private synchronized void ReReadConfig
{
long tempLastMod = configFile.lastModified();
if(lastModified == tempLastMod)
return;
<reread values here>
lastModified = tempLastMod;
}
public ChangingVariableSet GetValues()
{
ReReadConfig();
<return necessary values>
}
Run Code Online (Sandbox Code Playgroud)
(上面的代码没有经过测试,我只是希望得到一般的想法).
但我只是不喜欢堵的想法每一次请求的值,因为这似乎昂贵,以及我的应用程序有可能成为相当高负荷,有很多在未来线程的可能性.所以我有一个"好"的想法 - 在锁定之前检查文件是否被修改,然后再次在锁定方法内部,以避免在任何可能的情况下锁定:
public ChangingVariableSet GetValues()
{
if(lastModified == configFile.lastModified())
ReReadConfig();
<return …Run Code Online (Sandbox Code Playgroud) 在针对双重检查锁定方案的无序写入中提到的示例中(参考: IBM文章和维基百科文章)
在构造函数完全初始化之前,我无法理解为什么Thread1会在同步块中出现的简单原因.根据我的理解,创建"new"和调用构造函数应该按顺序执行,同步锁不应该释放,直到所有工作都没有完成.
请让我知道我在这里缺少什么.
java multithreading synchronization jls double-checked-locking
我有一个ConcurrentMap <String,SomeObject>对象。我想编写一个方法,如果存在,将返回SomeObject值,或者创建一个新的SomeObject,将其放入Map中,如果不存在则返回它。
理想情况下,我可以使用ConcurrentMap的putIfAbsent(key, new SomeObject(key)),但这意味着每次都创建一个新的SomeObject(key),这似乎非常浪费。
因此,我求助于以下代码,但不确定这是处理此问题的最佳方法:
public SomeValue getSomevalue(String key){
SomeValue result = concurrentMap.get(key);
if (result != null)
return result;
synchronized(concurrentMap){
SomeValue result = concurrentMap.get(key);
if (result == null){
result = new SomeValue(key);
concurrentMap.put(key, result);
}
return result;
}
}
Run Code Online (Sandbox Code Playgroud) 我知道线程安全单例的常见实现如下所示:
Singleton* Singleton::instance() {
if (pInstance == 0) {
Lock lock;
if (pInstance == 0) {
Singleton* temp = new Singleton; // initialize to temp
pInstance = temp; // assign temp to pInstance
}
}
return pInstance;
}
Run Code Online (Sandbox Code Playgroud)
但为什么他们说这是一个线程安全的实现呢?
例如,第一个线程可以通过 上的两个测试pInstance == 0,创建new Singleton并将其分配给temp指针,然后开始分配pInstance = temp(据我所知,指针分配操作不是原子的)。
同时,第二个线程测试第一个线程pInstance == 0,其中pInstance仅分配了一半。它不是 nullptr,但也不是一个有效的指针,然后从函数返回。这样的情况会发生吗?我在任何地方都没有找到答案,似乎这是一个非常正确的实现,但我什么也不明白
void undefined_behaviour_with_double_checked_locking()
{
if(!resource_ptr) #1
{
std::lock_guard<std::mutex> lk(resource_mutex); #2
if(!resource_ptr) #3
{
resource_ptr.reset(new some_resource); #4
}
}
resource_ptr->do_something(); #5
}
Run Code Online (Sandbox Code Playgroud)
如果一个线程看到另一个线程写入的指针,它可能看不到新创建的some_resource实例,导致调用do_something()操作不正确的值.这是由C++标准定义为数据竞争的竞争条件类型的示例,因此被指定为未定义的行为.
问题 >我已经看到上面的解释为什么代码具有导致竞争条件的双重检查锁定问题.但是,我仍然很难理解问题所在.也许一个具体的双线程逐步工作流程可以帮助我真正理解上面代码的竞争问题.
本书提到的解决方案之一如下:
std::shared_ptr<some_resource> resource_ptr;
std::once_flag resource_flag;
void init_resource()
{
resource_ptr.reset(new some_resource);
}
void foo()
{
std::call_once(resource_flag,init_resource); #1
resource_ptr->do_something();
}
#1 This initialization is called exactly once
Run Code Online (Sandbox Code Playgroud)
欢迎任何评论 - 谢谢
fallowing子句来自jetbrains.net在阅读了这篇以及网上的其他文章后,我仍然不明白在第一个线程进入锁之后如何返回null.有人确实理解它可以帮助我并以更人性化的方式解释它吗?
"考虑以下代码:
public class Foo
{
private static Foo instance;
private static readonly object padlock = new object();
public static Foo Get()
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Foo();
}
}
}
return instance;
}
};
Run Code Online (Sandbox Code Playgroud)
给定上面的代码,初始化Foo实例的写入可以被延迟,直到写入实例值,从而产生实例返回处于单元化状态的对象的可能性.
为了避免这种情况,必须使实例值易变."
我读过"双重锁定已破损"声明.但我想知道我是否通过函数创建对象,这样可以吗?
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null)
synchronized(this) {
if (helper == null)
helper = createHelper();
}
return helper;
}
private Helper createHelper() {
return new Helper();
}
// other functions and members...
}
Run Code Online (Sandbox Code Playgroud) 从发行说明中,checkstyle删除了Double-Checked锁定检查.
我很难理解为什么.他们不仅在发布说明中回复了这个问题,而且还在问题跟踪器中回复:
删除DoubleCheckedLocking检查,如在Java 5(及更高版本)中,使用volatile关键字解决问题.
我假设,如果checkstyle正在删除此警告,那是因为它不再有用.也就是说,错误将不再发生或另一个警告完成工作.但
我不明白为什么在Java 5中不再发生这样的错误,或者它是如何被另一个警告补充的.有人可以解释一下吗?
编辑:我理解添加volatile关键字如何解决问题.我担心的是:这个警告不是值得的吗?我正在考虑程序员使用上述锁定模式的情况,但忘记声明变量volatile.checkstyle还不应警告它吗?
java ×6
singleton ×3
c++ ×2
concurrency ×2
c# ×1
checkstyle ×1
enums ×1
jls ×1
lazy-loading ×1
swift ×1
swift4 ×1
volatile ×1