简体   繁体   中英

Synchronization in Java - Thinking in Java example

I read now Thinking in Java, chapter about synchronization and there is an example I cannot understand.

public abstract class IntGenerator {

    private volatile boolean canceled = false;

    public abstract int next();

    public void cancel() {
        canceled = true;
    }

    public boolean isCanceled() {
        return canceled;
    }
}

public class EvenGenerator extends IntGenerator {

    private int currentEvenValue = 0;

    final Object object = new Object();

    @Override
    public int next() {
        ++currentEvenValue;
        ++currentEvenValue;
        return currentEvenValue;
    }

    public static void main(String[] args) {
        EvenChecker.test(new EvenGenerator());
    }
}

public class EvenChecker implements Runnable {

    private IntGenerator generator;
    private final int id;

    public EvenChecker(IntGenerator generator, int id) {
        this.generator = generator;
        this.id = id;
    }

    @Override
    public void run() {
        while (!generator.isCanceled()) {
            int val = generator.next();
            if (val % 2 != 0) {
                System.out.println(val + " odd");
                generator.cancel();
            }
        }
    }

    public static void test(IntGenerator generator, int count) {
        System.out.println("To finish press Ctrl + C");
        final ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < count; i++) {
            executorService.execute(new EvenChecker(generator, i));
        }
    }

    public static void test(IntGenerator generator) {
        test(generator, 10);
    }
}

And example output is:

1239 odd
1237 odd
1239 odd

And I understand it. It means that 3 threads read currentValue after first increment.

Solution of this problem is:

public class SynchronizedEvenGenerator extends IntGenerator {

    private int currentEvenValue = 0;

    @Override
    public synchronized int next() {
        ++currentEvenValue;
        Thread.yield();
        ++currentEvenValue;
        return currentEvenValue;
    }

    public static void main(String[] args) {
        EvenChecker.test(new SynchronizedEvenGenerator());
    }
}

Now the program is working infinity without the mistake. I tried to synchronize only increments in this way:

public class SynchronizedEvenGenerator extends IntGenerator {

    private int currentEvenValue = 0;

    @Override
    public int next() {
        synchronized (this) {
            ++currentEvenValue;
            Thread.yield();
            ++currentEvenValue;
        }
        return currentEvenValue;
    }

    public static void main(String[] args) {
        EvenChecker.test(new SynchronizedEvenGenerator());
    }
}

But now example out put is:

345 odd

And I cannot understand why is it possible to read the odd value of currentValue if both increments are synchronized and any thread cannot read currentValue between first and second increment.

Why I get this output. How does work synchronized ?

Your final example's return currentEventValue; statement is not inside the synchronized block. So, suppose thread A and thread B both call next() :

Thread A:

  • Synchronizes,
  • Increments currentEventValue (value now is odd)
  • Increments currentEventValue (value is even again)
  • leaves the synchronized block.

Thread B:

  • Synchronizes
  • Increments currentEventValue (value now is odd)

Thread A:

  • returns currentEventValue (odd)

Thread B:

  • Increments currentEventValue (value is even again)
  • Leaves the synchronized block.
  • returns an even value.
  • currentEvenValue is 342
  • thread 1 enters the synchronized block
  • thread 2 tries to enter the synchronized block but must wait
  • thread 1 increments currentEvenValue twice, so the value is now 344
  • thread 1 leaves the synchronized block
  • thread 2 enters the synchronized block and increments currentEvenValue a first time, so the value is now 345
  • thread 1 reads the value of currentEvenValue, returns it, and prints it: 345

The rule is simple: all accesses to a shared state, read or write, must be synchronized.

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