简体   繁体   中英

Java visibility and synchronization - Thinking in Java example

I read now Thinking in Java, chapter about atomicity and visibility. There is an example I don't understand.

public class SerialNumberGenerator {
    private static volatile int serialNumber = 0;

    public static int nextSerialNumber() {
        return serialNumber++;
    }
}

class CircularSet {
    private int[] array;
    private int len;
    private int index = 0;

public CircularSet(int size) {
    array = new int[size];
    len = size;
    for (int i = 0; i < size; i++) {
        array[i] = -1;
    }
}

synchronized void add(int i) {
    array[index] = i;
    index = ++index % len;
}

synchronized boolean contains(int val) {
    for (int i = 0; i < len; i++) {
        if (array[i] == val)
            return true;
    }
    return false;
}

}

public class SerialNumberChecker {

private static final int SIZE = 10;
private static CircularSet serials = new CircularSet(1000);
private static ExecutorService exec = Executors.newCachedThreadPool();

static class SerialChecker implements Runnable {

    @Override
    public void run() {
        while (true) {
            int serial = SerialNumberGenerator.nextSerialNumber();
            if (serials.contains(serial)) {
                System.out.println("Duplicate: " + serial);
                System.exit(0);
            }
            serials.add(serial);
        }
    }
}

public static void main(String[] args) throws Exception {
    for (int i = 0; i < SIZE; i++) {
        exec.execute(new SerialChecker());
    }
}

}

example output:

Duplicate: 228

I don't understand how is it possible. Even method nextSerialNumber() is not synchronized and all thread generate different values each thread has own value of serial and each are different. So how is it possible to find duplicate. I cannot imagine of threads execution.

This example shows the post-increment operator is not atomic and not thread-safe.

What happens in this code is:

  • many (up to 100) threads are started, each executing the same code
  • in an infinite loop:
    • an unsynchronized method nextSerialNumber is called, which returns the result of the post-increment operator called on a static variable
    • a synchronized method contains is called, which checks if the returned value exists in the underlying collection
    • if yes, the program is terminated
    • if not, the value is added to the underlying collection

If the post-increment operation was thread-safe then the program would never print "Duplicate" and would never terminate, since every thread would be getting a different serial number value. This is not the case as two threads might get exactly the same serial number value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM