如何为我的 Java YAML-API 实现线程安全?
How to achieve thread-safety for my Java YAML-API?
示例 yaml 文件:example.yml
表示为 DreamYaml
对象。
该文件在初始化时被加载和解析。
现在让我们假设多个线程同时加载、编辑和保存该文件。这样就可以为同一个文件创建多个 DreamYaml
对象。在这种情况下实现线程安全的最佳方法是什么?
我的想法是在 load()
方法内部调用静态同步 lockFile()
方法,在 save()
方法内部调用 unlockFile()
方法。这样做的问题是,您每次都必须在最后调用 save()
才能解锁文件,而且这可能会比实际需要的时间更长地阻塞其他线程的文件。
我想最好的方法是在 save()
内部调用 lockFile()
方法,然后在那个锁定的部分内部,再次 load()
文件,以便当前对象在-内存值得到更新并匹配实际/'real' 当前值。然后简单地 save()
文件并释放锁。在这种情况下,我必须确保最近在内存中对值所做的更改不会被 load()
调用覆盖。我认为这种方法没有更多的主要缺点。 *编辑:我刚刚发现这种方法的主要缺点是它实际上不是线程安全的。感谢
**编辑:这是我想出的:
/**
* If you access the same yaml file from multiple threads, its recommended to lock the file before loading it. <br>
* Remember to {@link #unlockFile()} so that other threads can work with the file too. <br>
* If you don't do that, other threads will stay stuck at {@link #lockFile()} forever. <br>
* Example: <br>
* <pre>
* DreamYaml yaml = new DreamYaml("example.yml");
* yaml.lockFile();
* yaml.load();
* // Do changes to file here
* yaml.save();
* yaml.unlockFile();
* </pre>
*/
public synchronized void lockFile(){
if (file!=null){
ReentrantLock lock = null;
synchronized (pathsAndLocks){
if (pathsAndLocks.containsKey(file.getAbsolutePath()))
lock = pathsAndLocks.get(file.getAbsolutePath()); // If another thread has already the locked, the current thread will wait until it gets unlocked
else{
lock = new ReentrantLock();
pathsAndLocks.put(file.getAbsolutePath(), lock);
}
}
lock.lock();
}
}
/**
* If you access the same yaml file from multiple threads, its recommended to lock the file before loading it. <br>
* Remember to {@link #unlockFile()} so that other threads can work with the file too. <br>
* If you don't do that, other threads will stay stuck at {@link #lockFile()} forever. <br>
* Example: <br>
* <pre>
* DreamYaml yaml = new DreamYaml("example.yml");
* yaml.lockFile();
* yaml.load();
* // Do changes to file here
* yaml.save();
* yaml.unlockFile();
* </pre>
*/
public synchronized void unlockFile(){
if (file!=null){
ReentrantLock lock = null;
synchronized (pathsAndLocks){
if (pathsAndLocks.containsKey(file.getAbsolutePath())){
lock = pathsAndLocks.get(file.getAbsolutePath()); // If another thread has already the locked, the current thread will wait until it gets unlocked
lock.unlock();
if(!lock.hasQueuedThreads())
pathsAndLocks.remove(file.getAbsolutePath());
}
}
}
}
在上面的代码中,pathsAndLocks
变量是一个包含 yaml 文件路径及其锁的哈希图。这意味着每个文件有一个 ReentrantLock。现在用户可以在加载 yaml 文件之前调用 lockFile()
以实现线程安全,并在完成后调用 unlockFile()
。
但我不完全确定...那么您对此有何看法?
关于我的 Java-YAML API 的更多详细信息:https://github.com/Osiris-Team/Dream-Yaml
仅在保存非线程安全时锁定数据。
假设数据中的某项包含值 0
。两个线程同时想要将值增加 1。可能会出现以下经典竞争条件:
两个线程同时读入 0
。两者都将新值计算为 1
。一个线程获取锁,加载当前数据,将项目设置为 1
,然后保存。其他线程然后获取锁,加载更新的数据,用1
替换项目 因为新值已经计算,然后保存。结果是文件中的项目是 1
而它应该是 2
.
无论是将值保存到文件还是保存在内存中,为了实现线程安全,从读取值到时间您提交更新后的值。
实现线程安全的最佳方法是什么这个问题无法得到笼统的回答。这取决于您的用例,可能需要对系统行为进行广泛分析。如前所述,从加载到保存的排他锁将起作用(当然不会检测文件的外部修改)。
不清楚为什么要一直保存和加载数据,在启动时加载一次文件并将其保存在内存中似乎要简单得多。当您进行修改时,您保存文件但将其保存在内存中。如果文件 a) 很大并且 b) 修改很少发生,这只会很糟糕。
示例 yaml 文件:example.yml
表示为 DreamYaml
对象。
该文件在初始化时被加载和解析。
现在让我们假设多个线程同时加载、编辑和保存该文件。这样就可以为同一个文件创建多个 DreamYaml
对象。在这种情况下实现线程安全的最佳方法是什么?
我的想法是在 load()
方法内部调用静态同步 lockFile()
方法,在 save()
方法内部调用 unlockFile()
方法。这样做的问题是,您每次都必须在最后调用 save()
才能解锁文件,而且这可能会比实际需要的时间更长地阻塞其他线程的文件。
我想最好的方法是在 save()
内部调用 lockFile()
方法,然后在那个锁定的部分内部,再次 load()
文件,以便当前对象在-内存值得到更新并匹配实际/'real' 当前值。然后简单地 save()
文件并释放锁。在这种情况下,我必须确保最近在内存中对值所做的更改不会被 load()
调用覆盖。我认为这种方法没有更多的主要缺点。 *编辑:我刚刚发现这种方法的主要缺点是它实际上不是线程安全的。感谢
**编辑:这是我想出的:
/**
* If you access the same yaml file from multiple threads, its recommended to lock the file before loading it. <br>
* Remember to {@link #unlockFile()} so that other threads can work with the file too. <br>
* If you don't do that, other threads will stay stuck at {@link #lockFile()} forever. <br>
* Example: <br>
* <pre>
* DreamYaml yaml = new DreamYaml("example.yml");
* yaml.lockFile();
* yaml.load();
* // Do changes to file here
* yaml.save();
* yaml.unlockFile();
* </pre>
*/
public synchronized void lockFile(){
if (file!=null){
ReentrantLock lock = null;
synchronized (pathsAndLocks){
if (pathsAndLocks.containsKey(file.getAbsolutePath()))
lock = pathsAndLocks.get(file.getAbsolutePath()); // If another thread has already the locked, the current thread will wait until it gets unlocked
else{
lock = new ReentrantLock();
pathsAndLocks.put(file.getAbsolutePath(), lock);
}
}
lock.lock();
}
}
/**
* If you access the same yaml file from multiple threads, its recommended to lock the file before loading it. <br>
* Remember to {@link #unlockFile()} so that other threads can work with the file too. <br>
* If you don't do that, other threads will stay stuck at {@link #lockFile()} forever. <br>
* Example: <br>
* <pre>
* DreamYaml yaml = new DreamYaml("example.yml");
* yaml.lockFile();
* yaml.load();
* // Do changes to file here
* yaml.save();
* yaml.unlockFile();
* </pre>
*/
public synchronized void unlockFile(){
if (file!=null){
ReentrantLock lock = null;
synchronized (pathsAndLocks){
if (pathsAndLocks.containsKey(file.getAbsolutePath())){
lock = pathsAndLocks.get(file.getAbsolutePath()); // If another thread has already the locked, the current thread will wait until it gets unlocked
lock.unlock();
if(!lock.hasQueuedThreads())
pathsAndLocks.remove(file.getAbsolutePath());
}
}
}
}
在上面的代码中,pathsAndLocks
变量是一个包含 yaml 文件路径及其锁的哈希图。这意味着每个文件有一个 ReentrantLock。现在用户可以在加载 yaml 文件之前调用 lockFile()
以实现线程安全,并在完成后调用 unlockFile()
。
但我不完全确定...那么您对此有何看法?
关于我的 Java-YAML API 的更多详细信息:https://github.com/Osiris-Team/Dream-Yaml
仅在保存非线程安全时锁定数据。
假设数据中的某项包含值 0
。两个线程同时想要将值增加 1。可能会出现以下经典竞争条件:
两个线程同时读入 0
。两者都将新值计算为 1
。一个线程获取锁,加载当前数据,将项目设置为 1
,然后保存。其他线程然后获取锁,加载更新的数据,用1
替换项目 因为新值已经计算,然后保存。结果是文件中的项目是 1
而它应该是 2
.
无论是将值保存到文件还是保存在内存中,为了实现线程安全,从读取值到时间您提交更新后的值。
实现线程安全的最佳方法是什么这个问题无法得到笼统的回答。这取决于您的用例,可能需要对系统行为进行广泛分析。如前所述,从加载到保存的排他锁将起作用(当然不会检测文件的外部修改)。
不清楚为什么要一直保存和加载数据,在启动时加载一次文件并将其保存在内存中似乎要简单得多。当您进行修改时,您保存文件但将其保存在内存中。如果文件 a) 很大并且 b) 修改很少发生,这只会很糟糕。