Traditional synchronized:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class FizzBuzz {
private int n;
private int currentNumber = 1;

public FizzBuzz(int n) {
this.n = n;
}

// printFizz.run() outputs "fizz".
public synchronized void fizz(Runnable printFizz) throws InterruptedException {
while (currentNumber <= n) {
if (currentNumber %3 != 0 || currentNumber %5 == 0) {
wait();
continue;
}
printFizz.run();
currentNumber += 1;
notifyAll();
}
}

// printBuzz.run() outputs "buzz".
public synchronized void buzz(Runnable printBuzz) throws InterruptedException {
while (currentNumber <= n) {
if (currentNumber %3 == 0 || currentNumber %5 != 0) {
wait();
continue;
}
printBuzz.run();
currentNumber += 1;
notifyAll();
}
}

// printFizzBuzz.run() outputs "fizzbuzz".
public synchronized void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
while (currentNumber <= n) {
if (currentNumber %15 != 0) {
wait();
continue;
}
printFizzBuzz.run();
currentNumber += 1;
notifyAll();
}
}

// printNumber.accept(x) outputs "x", where x is an integer.
public synchronized void number(IntConsumer printNumber) throws InterruptedException {
while (currentNumber <= n) {
if (currentNumber %3 == 0 || currentNumber %5 == 0) {
wait();
continue;
}
printNumber.accept(currentNumber);
currentNumber += 1;
notifyAll();
}
}
}

与题目相关:

  1. continue : jump outside while directly, i.e. synchronized method finishes running.
  2. synchronized: wait(), notifyAll()都是和多线程相关的,所以方法肯定要加synchronized, 否则Thrown exception java.lang.IllegalMonitorStateException

In synchronized methods, a thread is allowed to keep running until it either leaves the method or it calls wait(). No other thread can run until this happens.

Threads can be in one of 4 states:

  1. Currently running.
  2. Waiting to obtain the monitor so it can run (This is a different type of blocking.)
  3. Blocked, waiting to be notified. (called wait())
  4. Exited, will never run again.

In the standard model, the scheduler is able to move a thread from 1 to 2 and then another from 2 to 1 whenever it pleases. But synchronized prevents this from happening.

……

just need to ensure that:

  1. thread NEVER gives up the monitor (i.e. calls wait()) when it was actually its turn to run.
  2. notifyAll() is always called whenever an important change is made, ensuring that everybody will get their chance to see it. This tells them they can run as soon as they can get that monitor.

i.e. synchronized methods:

  1. run, state changed: notifyAll();
  2. always hold monitor until end, then wait();

or better:

Semaphore:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class FizzBuzz {
private int n;
private Semaphore sem, sem3, sem5, sem15;

public FizzBuzz(int n) {
this.n = n;
sem = new Semaphore(1);
sem3 = new Semaphore(0);
sem5 = new Semaphore(0);
sem15 = new Semaphore(0);
}

// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
for (int i = 3; i <= n; i += 3) {
sem3.acquire();
printFizz.run();
if ((i + 3) %5 == 0) i += 3;
sem.release();
}
}

// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
for (int i = 5; i <= n; i += 5) {
sem5.acquire();
printBuzz.run();
if ((i + 5) %3 == 0) i += 5;
sem.release();
}
}

// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
for (int i = 15; i <= n; i += 15) {
sem15.acquire();
printFizzBuzz.run();
sem.release();
}
}

// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; ++i) {
sem.acquire();
if (i % 15 == 0) sem15.release();
else if (i %3 == 0) sem3.release();
else if (i %5 == 0) sem5.release();
else {
printNumber.accept(i);
sem.release();
}
}
}
}