如果满足条件,则以原子方式写入两个变量

时间:2015-11-13 08:07:38

标签: java multithreading synchronization

我有以下类将由多个线程使用:

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进行优化。

执行两次批量计算不会对数据结构造成损害,但会浪费处理器的时间。

我应该如何以更有效的方式处理这个问题?

2 个答案:

答案 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计算期间很可能会更新数据,您需要在某个时刻中断循环并执行这些计算锁定:性能下降很糟糕,但饥饿更糟糕。