Java 限制对方法的并发访问
我对限制对方法的并发访问有问题。我有一个方法Java 限制对方法的并发访问,java,concurrency,static,synchronized,Java,Concurrency,Static,Synchronized,我对限制对方法的并发访问有问题。我有一个方法MyService,可以从很多地方多次调用。此方法必须返回一个字符串,该字符串应根据某些规则进行更新。为此,我有一个updatedString类。在获取字符串之前,它会确保字符串已更新,如果未更新,则会对其进行更新。许多线程可以同时读取字符串,但如果字符串过期,则只有一个线程可以同时续订该字符串 public final class updatedString { private static final String UPstring; priva
MyService
,可以从很多地方多次调用。此方法必须返回一个字符串
,该字符串应根据某些规则进行更新。为此,我有一个updatedString
类。在获取字符串
之前,它会确保字符串
已更新,如果未更新,则会对其进行更新。许多线程可以同时读取字符串
,但如果字符串
过期,则只有一个线程可以同时续订该字符串
public final class updatedString {
private static final String UPstring;
private static final Object lock = new Object();
public static String getUpdatedString(){
synchronized(lock){
if(stringNeedRenewal()){
renewString();
}
}
return getString();
}
...
这个很好用。如果我有7个线程获取字符串,它保证在必要时只有一个线程更新字符串
我的问题是,让所有这些都是静态的好主意吗?为什么不呢?它快吗?有更好的方法吗?
我读过这样的帖子:
这表明静态可变变量不是一个好主意,静态类也是。但我看不到代码中有任何死锁或更好的有效解决方案。只有一些线程必须等到字符串
更新(如果需要)或等待其他线程离开同步块(这会导致一个小的延迟)
如果该方法不是静态的,那么我就有一个问题,因为同步方法只对线程正在使用的当前实例起作用,所以这将不起作用。同步的方法也不起作用,看起来锁是特定于实例的,而不是特定于类的。
另一个解决方案是使用一个避免创建多个实例的单例,然后使用一个同步的非静态类,但我不太喜欢这个解决方案
其他信息:
stringNeedRenewal()
虽然必须从数据库中读取,但成本并不太高renewString()
相反,它非常昂贵,必须从数据库的多个表中读取数据才能最终得出答案。字符串
需要任意更新,但这种情况并不经常发生(从每小时一次到每周一次)
@forsvarir让我想。。。我认为他/她是对的<代码>返回getString()代码>必须位于同步方法内。乍一看,它似乎可以脱离它,因此线程将能够同时读取它,但如果一个线程在调用getString()
时停止运行,而另一个线程部分执行renewString()
,会发生什么情况?我们可能会遇到这种情况(假设使用单处理器):
getString()
。操作系统
开始将字节复制到内存中
被退回renewString()
,
更改中的原始字符串
记忆
字符串
!!所以它复制了一个
从旧弦和另一根弦分离
从新的开始正如@Jeremy Heiler指出的,这是缓存的抽象问题。如果缓存已旧,请续订它。如果没有,请使用它。更好的办法是将问题描述成这样,而不是一个
字符串(或者设想有两个字符串而不是一个)。那么,如果有人在修改缓存的同时进行读取,会发生什么情况呢?只要您的锁是静态的,其他一切都不必是静态的,事情就会像现在一样正常运行首先,您可以移除锁和同步块,只需使用:
public static synchronized String getUpdatedString(){
if(stringNeedRenewal()){
renewString();
}
return getString();
}
这将在UpdatedString.class
对象上同步
您可以做的另一件事是使用双重检查锁定来防止不必要的等待。将字符串声明为volatile,并且:
public static String getUpdatedString(){
if(stringNeedRenewal()){
synchronized(lock) {
if(stringNeedRenewal()){
renewString();
}
}
}
return getString();
}
然后,不管是否使用static,它似乎应该是静态的,因为您希望在没有任何特定实例的情况下调用它。我建议您研究一个静态的。(是否执行由您决定。)这样您可以同时执行多个读取操作
以下是文档中的示例:
class CachedData {
Object data;
volatile boolean cacheValid;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
// Recheck state because another thread might have acquired
// write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
rwl.writeLock().unlock(); // Unlock write, still hold read
}
use(data);
rwl.readLock().unlock();
}
}
这并不完全是您想要的,而且我不是Java专家,所以请对此持保留态度:)
也许您提供的代码示例是人为设计的,但如果不是,我不清楚该类的用途是什么。您只需要一个线程将字符串更新为新值。为什么?这是为了省力吗(因为您更愿意在其他方面使用处理器周期)?是否要保持一致性(一旦达到某个点,字符串必须更新)
所需更新之间的周期有多长
查看您的代码
public final class updatedString {
private static final String UPstring;
private static final Object lock = new Object();
public static String getUpdatedString(){
synchronized(lock){
// One thread is in this block at a time
if(stringNeedRenewal()){
renewString(); // This updates the shared string?
}
}
// At this point, you're calling out to a method. I don't know what the
// method does, I'm assuming it just returns UPstring, but at this point,
// you're no longer synchronized. The string actually returned may or may
// not be the same one that was present when the thread went through the
// synchronized section hence the question, what is the purpose of the
// synchronization...
return getString(); // This returns the shared string?
}
正确的锁定/优化取决于放置它们的原因、需要写入的可能性以及Paulo所说的操作成本
对于一些写操作很少的情况,并且显然取决于renewString的功能,可能需要使用乐观的写方法。其中,每个线程检查是否需要刷新,继续在本地执行更新,然后仅在最后将值分配给正在读取的字段(如果采用这种方法,则需要跟踪更新的时间)。这将加快读取速度,因为“字符串是否需要更新”的检查可以在同步部分之外执行。根据具体情况,可以使用各种其他方法…我建议您使用私有静态最终对象锁=新对象();保持静态,这样您就可以锁定相同的对象。其他一切都不必是静态的,如果不是