试图弄清楚如何在Java中正确地进行并发。我有两组任务。组内的任务不会相互干扰,因此可以同时运行而不会出现问题。但是,当第二组的任何任务正在运行时,不应运行一个组的任务。
第一组每10分钟运行一次。 第二组每小时运行一次。 我使用两个ScheduledExecutorService来安排这两个组。
理想情况下,每个任务都需要检查第二组中的任何任务是否正在运行,等待或完成,然后再运行。
我尝试用锁来解决这个问题,并想出了类似的东西:
lock1.lock();
try {
while (!lock2.tryLock()) {
wait();
}
for (int i = 0; i < 20; i++)
System.out.println("Test1 @@@@@@@@@");
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
lock1.unlock();
}
}
第一组共享lock1,第二组锁定2。但是这样,我强迫第一组的所有任务等待公共锁。
解决这个问题的优雅方法是什么?
答案 0 :(得分:0)
尝试使用两个独立的Lock
来完成这样的任务非常复杂且容易出错。您需要的内容与ReadWriteLock
非常相似,其中两个Lock
相互排斥。唯一的区别是两个锁应该是非排他性的,就像ReadLock
一样。
这种锁定对可以直接实现,类似于ReentrantReadWriteLock
的工作方式。下面的示例使用单个原子int状态值,该值表示可重入锁定计数与值的符号相结合,告知哪个组拥有锁定。与大多数锁实现一样,值为零表示锁是免费的。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class TwoGroupLock {
private final Sync sync=new Sync();
private final Lock group1, group2;
public TwoGroupLock() {
group1=new L(+1);
group2=new L(-1);
}
public Lock getGroup1Lock() {
return group1;
}
public Lock getGroup2Lock() {
return group2;
}
private final class L implements Lock {
private final int group;
L(int i) { group=i; }
public void lock() { sync.acquireShared(group); }
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(group);
}
public boolean tryLock() { return sync.tryAcquireShared(group)>=0; }
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(group, unit.toNanos(time));
}
public void unlock() { sync.releaseShared(group); }
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
private final class Sync extends AbstractQueuedSynchronizer {
protected @Override int tryAcquireShared(int group) {
for(;;) {
final int state=getState();
if(state!=0 && (state^group)<0) return -1;
if(compareAndSetState(state, state+group)) return +1;
}
}
protected @Override boolean tryReleaseShared(int group) {
for(;;) {
final int state=getState();
if(state==0 || (state^group)<0)
throw new IllegalMonitorStateException();
if(compareAndSetState(state, state-group)) return true;
}
}
}
}
没有必要了解使用该类的所有内部细节。与ReadWriteLock类似,您有两种获取相关锁的方法,如ReadLock
,多线程可以获取锁定,但前提是没有线程持有另一个锁。这两个锁是对称的。
答案 1 :(得分:0)
为此目的使用条件队列似乎是一个很好的主意,特别是在等待条件变为真或假时。
但是,我建议您使用Condition
中的Lock#newCondition()
个库。
以下是我头顶的一个例子:
public class Waiter {
// Happens-before is strong enough to make sure the view
// of running is visible to other threads
// Even if the lock is not held, writes to a boolean
// is atomic and non-compound, therefore, no synchronization
// is needed because threads cannot be interleaved
private boolean running = false;
private final ReentrantLock taskLock = new ReentrantLock();
private final Condition condition = this.taskLock.newCondition();
public void check() throws InterruptedException {
if (!this.taskLock.isLocked()) return;
while (this.running) this.condition.await();
}
public void run() {
this.taskLock.lock();
this.running = true;
}
public void done() {
// Wait for the while loop to finish,
// Release the threads held in the queue,
// then the lock can be released
try {
this.running = false;
this.condition.signalAll();
} finally {
this.taskLock.unlock();
}
}
}
样本用法:
public static final Waiter WAITER = new Waiter();
// First task
WAITER.run();
try {
// ...
} finally {
WAITER.done();
}
// Second task
try {
WAITER.check();
} catch (InterruptedException x) {
Thread.currentThread().interrupt();
x.printStackTrace();
}
// ...