0

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?

2 Answers 2

2

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.
Sign up to request clarification or add additional context in comments.

Comments

1
  • 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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.