import java.util.Arrays; import java.util.concurrent.ThreadLocalRandom; public class AtomicUpdates { private static final int NUM_BUCKETS = 10; public static class Buckets { private final int[] data; public Buckets(int[] data) { this.data = data.clone(); } public int getBucket(int index) { synchronized (data) { return data[index]; } } public int transfer(int srcIndex, int dstIndex, int amount) { if (amount < 0) throw new IllegalArgumentException("negative amount: " + amount); if (amount == 0) return 0; synchronized (data) { if (data[srcIndex] - amount < 0) amount = data[srcIndex]; if (data[dstIndex] + amount < 0) amount = Integer.MAX_VALUE - data[dstIndex]; if (amount < 0) throw new IllegalStateException(); data[srcIndex] -= amount; data[dstIndex] += amount; return amount; } } public int[] getBuckets() { synchronized (data) { return data.clone(); } } } private static long getTotal(int[] values) { long total = 0; for (int value : values) { total += value; } return total; } public static void main(String[] args) { ThreadLocalRandom rnd = ThreadLocalRandom.current(); int[] values = new int[NUM_BUCKETS]; for (int i = 0; i < values.length; i++) values[i] = rnd.nextInt() & Integer.MAX_VALUE; System.out.println("Initial Array: " + getTotal(values) + " " + Arrays.toString(values)); Buckets buckets = new Buckets(values); new Thread(() -> equalize(buckets), "equalizer").start(); new Thread(() -> transferRandomAmount(buckets), "transferrer").start(); new Thread(() -> print(buckets), "printer").start(); } private static void transferRandomAmount(Buckets buckets) { ThreadLocalRandom rnd = ThreadLocalRandom.current(); while (true) { int srcIndex = rnd.nextInt(NUM_BUCKETS); int dstIndex = rnd.nextInt(NUM_BUCKETS); int amount = rnd.nextInt() & Integer.MAX_VALUE; buckets.transfer(srcIndex, dstIndex, amount); } } private static void equalize(Buckets buckets) { ThreadLocalRandom rnd = ThreadLocalRandom.current(); while (true) { int srcIndex = rnd.nextInt(NUM_BUCKETS); int dstIndex = rnd.nextInt(NUM_BUCKETS); int amount = (buckets.getBucket(srcIndex) - buckets.getBucket(dstIndex)) / 2; if (amount >= 0) buckets.transfer(srcIndex, dstIndex, amount); } } private static void print(Buckets buckets) { while (true) { long nextPrintTime = System.currentTimeMillis() + 3000; long now; while ((now = System.currentTimeMillis()) < nextPrintTime) { try { Thread.sleep(nextPrintTime - now); } catch (InterruptedException e) { return; } } int[] bucketValues = buckets.getBuckets(); System.out.println("Current values: " + getTotal(bucketValues) + " " + Arrays.toString(bucketValues)); } } }