AMZ
AMZ

Reputation: 362

compareAndSet() does not work as expected

I wrote my own AtomicDouble class and I also have a BankAccount class that does two simple withdrawals and deposits operations and it has an AtomicDouble instance(balance). The problem with my code is that when I call the addAndGet method in deposit(), the program falls into an infinite loop, and compareAndSet() never returns the true value, but when I debugged this, currentValue and the value from atomic.get () were equal, but this method does not understand.

The interesting thing is that when I put if (atomic.get()==currentValue) instead of if (atomic.compareAndSet(currentValue, nextValue)), the program runs properly.

public class AtomicDouble extends Number {

private final AtomicReference<Double> atomic;

public AtomicDouble() {
    this(0.0);
}

public AtomicDouble(double initialValue) {
    atomic = new AtomicReference<>(initialValue);
}

public final double addAndGet(double delta) {
    while (true) {
        double currentValue = atomic.get();
        double nextValue = currentValue + delta;
        if (atomic.compareAndSet(currentValue, nextValue))
            return nextValue;
    }
}

public final double incrementAndGet() {
    return addAndGet(1);
}

public final void set(double newValue) {
    atomic.set(newValue);
}

public final double get() {
    return atomic.get();
}


public final double getAndSet(double newValue) {
    return atomic.getAndSet(newValue);
}


public float floatValue() {
    return (float) get();
}

@Override
public double doubleValue() {
    return get();
}

public int intValue() {
    return (int) get();
}

public long longValue() {
    return (long) get();
}

public String toString() {
    return Double.toString(get());
}

}

public class BankAccount {
private final AtomicDouble balance;
private String accountNumber;

public BankAccount(double balance, String accountNumber) {
    this.balance = new AtomicDouble(balance);
    this.accountNumber = accountNumber;

}

public void deposit(double number, String color) {
    System.out.println(color + "deposit  " + number + " current balance=" + balance.addAndGet(number));
}

public void withdraw(double number, String color) {
    if (this.balance.get() - number >= 0) {
        System.out.println(color + "Withdraw " + number + " current balance=" + balance.addAndGet(-number));
        return;
    }
    System.out.println(color + "Not enough balance");
}


public static void main(String[] args) {

    BankAccount bankAccount = new BankAccount(1000.0, "4234236");

    ExecutorService threadsPool = Executors.newFixedThreadPool(2);
    threadsPool.execute(new Runnable() {
        @Override
        public void run() {
            bankAccount.deposit(300.0, ThreadColor.ANSI_YELLOW);
            bankAccount.withdraw(50.0, ThreadColor.ANSI_YELLOW);
        }
    });

    threadsPool.execute(new Runnable() {
        @Override
        public void run() {
            bankAccount.deposit(203.75, ThreadColor.ANSI_BLUE);
            bankAccount.withdraw(100.0, ThreadColor.ANSI_BLUE);
        }
    });

    threadsPool.shutdown();
}

}

output: There is no output

Upvotes: 1

Views: 365

Answers (1)

passer-by
passer-by

Reputation: 1113

I would suppose it is because of autoboxing. You can't have a reference to double, you have a reference to Double.

The operands get "reboxed" each time around the loop and therefore references are never identical. That is, the reference in currentValue is never the same as the reference in atomic.

Try using currentValue reference types.

public final double addAndGet(double delta) {
    while (true) {
        Double currentValue = atomic.get();
        Double nextValue = currentValue + delta;
        if (atomic.compareAndSet(currentValue, nextValue))
            return nextValue;
    }
}

(Fortunately, Double is an immutable type, otherwise this would have a race hazard)

Upvotes: 4

Related Questions