我有以下类将由多个线程使用:
public class Container
private volatile Date date;
private int amount;
private final Object lock = new Object();
public void update(int amount){
int actualAmount;
if(check(date)){
//do some BULK computation to compute the actualAmount
synchronized(lock){
date = new Date();
this.amount = actualAmount;
}
}
}
private boolean check(Date date){
synchronized(lock){
//reading from the date and returning true if the date is expired
}
}
}
但我不确定正确性。首先,我将Date
字段声明为volatile
,以便在执行条件检查时观察更改。但是,在进行批量计算时,第二个线程可能会尝试执行更新,从而导致数据争用。
我不想将BULK
计算放入synchronized块中,因为它包括调用几个外来方法并阻止JVM进行优化。
执行两次批量计算不会对数据结构造成损害,但会浪费处理器的时间。
我应该如何以更有效的方式处理这个问题?
答案 0 :(得分:1)
当批量计算完成并且另一个线程在此期间已经更改了金额时,我们别无选择,只能重新计算金额(重新运行此批量操作),是吗?
public void update(int amount) {
int actualAmount;
if (check(date)){
//do some BULK computation to compute the actualAmount
synchronized(lock) {
if (check(date)) {
date = new Date();
this.amount = actualAmount;
} else {
update(amount);
}
}
}
}
当两个线程执行此批量操作并且其中一个应该重新运行它时,是不是浪费了CPU时间?
累积此更新(金额)以及某些线程需要读取正确数量然后重新计算(运行批量操作)可能更有效。
什么线程会读到这个?它需要最新信息吗? 了解答案可能有助于设计更好的解决方案。
更新1。 顺便说一句,这种递归重新计算可能需要一遍又一遍。因此,最正确的方法是将整个update()主体包装到synchronized语句中。
答案 1 :(得分:1)
这样的事情:
public class Container
private Date date;
private int amount;
private final Object lock = new Object();
public void update(int amount){
int actualAmount;
Date oldDate;
// Loop until no one call update() during our computations
for(oldDate = checkDate(); oldDate != null; oldDate = checkDate()){
//do some BULK computation to compute the actualAmount
synchronized(lock){
if(date == oldDate){
// No one update data inbetween
date = new Date();
this.amount = actualAmount;
break;
}
}
}
}
/* Return this.date if it is expired or null otherwise */
private Date checkDate(void){
synchronized(lock){
// Check .date and return appropriate value
}
}
}
如果过期,我们会存储.date
的值。这样我们可以检查,当我们执行BULK计算时是否有人更新了数据。如果有,我们重复数据检查和计算。
如果您必须在lock
中使用checkDate()
,则无需将.date
声明为 volatile 。如果您能够检查日期而不锁定,您可能(取决于实际算法)应该还原 volatile 修饰符。
注意,如果您希望update()
可以被非常密集地调用,那么在BULK计算期间很可能会更新数据,您需要在某个时刻中断循环并执行这些计算锁定:性能下降很糟糕,但饥饿更糟糕。