在下面的测试用例中,update()
方法在类外部调用,该方法仅在每小时运行一个线程。但是有多个线程同时调用getKey1()
和getKey2()
。
对于这个用例:
最重要的是,由于getKey1()
和getKey2()
几乎同时呼叫,我们必须确保
我们不希望存在这样的情况:更新key1并获取旧的key2,但即使我们已经更新它,也可以获得旧的key1和key2,即使我们已经更新了它。
public class Test {
private Lock lock = new ReentrantLock();
private AtomicBoolean flag1 = new AtomicBoolean(false);
private AtomicBoolean flag2 = new AtomicBoolean(false);
private volatile String key1;
private volatile String key2;
public Test(String key1, String key2) {
this.key1 = key1;
this.key2 = key2;
}
public void update() {
lock.lock();
try {
flag1.compareAndSet(false, true);
flag2.compareAndSet(false, true);
} finally {
lock.unlock();
}
}
public String getKey1() {
// TODO if the lock is holding... block over here
if (flag1.get()) {
// doing something for key 1
key1 = getFromFile();
flag1.set(false);
}
return key1;
}
public String getKey2() {
// TODO if the lock is holding... block over here
if (flag2.get()) {
// doing something for key 1
key2 = getFromFile();
flag2.set(false);
}
return key1;
}
}
我的想法是:
getKey1()
和getKey2()
,等待获取更新密钥getKey1()
和getKey2()
都应该可以直接返回。有人对实施有任何想法吗?
答案 0 :(得分:0)
正如已经说过java.util.concurrent.locks.ReentrantReadWriteLock
最适合你。
将它放在你的例子中:
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Test {
private ReadWriteLock lock = new ReentrantReadWriteLock();
private AtomicBoolean flag1 = new AtomicBoolean(false);
private AtomicBoolean flag2 = new AtomicBoolean(false);
private volatile String key1;
private volatile String key2;
public Test(String key1, String key2) {
this.key1 = key1;
this.key2 = key2;
}
public void update() {
Lock writeLock = lock.writeLock();
try {
flag1.compareAndSet(false, true);
flag2.compareAndSet(false, true);
} finally {
writeLock.unlock();
}
}
public String getKey1() {
Lock readLock = lock.readLock();
try {
if (flag1.get()) {
// doing something for key 1
key1 = getFromFile();
flag1.set(false);
}
return key1;
} finally {
readLock.unlock();
}
}
public String getKey2() {
Lock readLock = lock.readLock();
try {
if (flag2.get()) {
// doing something for key 1
key2 = getFromFile();
flag2.set(false);
}
return key1;
} finally {
readLock.unlock();
}
}
}
来自ReadWriteLock
界面的文档:
读锁可以由多个读取器线程同时保持, 只要没有作家。写锁是独占的。
答案 1 :(得分:0)
以下ReadWriteLock示例的更新,我们必须在开始读/写操作之前获取锁。由于我无权编辑样本,因此发布新答案。
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Test {
private ReadWriteLock lock = new ReentrantReadWriteLock();
private AtomicBoolean flag1 = new AtomicBoolean(false);
private AtomicBoolean flag2 = new AtomicBoolean(false);
private volatile String key1;
private volatile String key2;
public Test(String key1, String key2) {
this.key1 = key1;
this.key2 = key2;
}
public void update() {
Lock writeLock = lock.writeLock();
try {
writeLock.lock();
flag1.compareAndSet(false, true);
flag2.compareAndSet(false, true);
} finally {
writeLock.unlock();
}
}
public String getKey1() {
Lock readLock = lock.readLock();
try {
readLock.lock();
if (flag1.get()) {
// doing something for key 1
key1 = getFromFile();
flag1.set(false);
}
return key1;
} finally {
readLock.unlock();
}
}
public String getKey2() {
Lock readLock = lock.readLock();
try {
readLock.lock();
if (flag2.get()) {
// doing something for key 1
key2 = getFromFile();
flag2.set(false);
}
return key1;
} finally {
readLock.unlock();
}
}
}
答案 2 :(得分:0)
这肯定是ReadWriteLock
应该有帮助的情况。
许多并发读者可以在极小的争用下进行操作,并且编写器很少获得写锁定和更新。
然而,提出的关键问题是线程不能一次读取一个旧密钥和一个新密钥。
这是一个经典问题。最简单的解决方案是更新更新线程中的两个密钥。
ReentrantReadWriteLock rwlock=new ReentrantReadWriteLock(true);
//If in doubt require a fair lock to avoid live-locking writes...
//...
public void update() {
String new1=getFromFile();
String new2=getFromFile();
//That's the heavy lifting. Only now acquire the lock...
Lock wlock=rwlock.writeLock();
wlock.lock();
try {
key1=new1;
key2=new2;
} finally {
wlock.unlock();
}
}
在读者中:
public String[] getKeys() {
String k1;
String k2;
Lock rlock=rwlock.readLock();
rlock.lock();
try {
k1=key1;
k2=key2;
} finally {
rlock.unlock();
}
String[] r={k1,k2};
return r;
}
读写锁具有两个连接的锁。多个读取器可以获取读锁定,但写锁定不包括读取器和其他写入器。这是一种常见的情况,已经建立了解决方案。
从>强获取锁之前的文件获取锁定可能很重要,因为I / O通常相对较慢,您应该在最短的时间内保持锁定。
从同一个锁的获取中返回两个键也很重要。在getKey1()
和getKey2()
的来电之间没有其他简单的方法可以阻止更新。
由于锁的内存屏障保证,您不再需要使用volatile
关键字(请参阅文档)。
您实际上并不需要可重入锁定,但这是开箱即用的读写锁定。
在这种情况下,称为顺序锁(Seqlock)的东西可能有所帮助,但这将涉及更多编码,并且可能在这里过度设计。
参考文献:
https://en.wikipedia.org/wiki/Seqlock
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReadWriteLock.html
答案 3 :(得分:-2)
使用tryLock()
public String getKey1() {
if (lock.tryLock())
{
// Got the lock
try
{
// Process record
}
finally
{
// Make sure to unlock so that we don't cause a deadlock
lock.unlock();
}
}
}
如果您不希望getKey1()或getKey2()执行锁定。您可以使用以下方法,
static boolean isLocked=false;
public void update() {
lock.lock();
isLocked = true;
try {
flag1.compareAndSet(false, true);
flag2.compareAndSet(false, true);
} finally {
lock.unlock();
isLocked = false;
}
}
public String getKey1() {
while(isLocked); #wait till the lock release.
if (flag1.get()) {
// doing something for key 1
key1 = getFromFile();
flag1.set(false);
}
return key1;
}
参考:how-do-determine-if-an-object-is-locked-synchronized-so-not-to-block-in-java