synchronized 实现
class ReentrantTest {private int n;private volatile int flag = 1;private Object lock = new Object();public ReentrantTest(int n) {this.n = n;}public void zero(IntConsumer printNumber) throws InterruptedException{for(int i=1;i<=n;){synchronized (lock){if(flag%4==1 ||flag%4==3){System.out.println(0);flag++;i++;lock.notifyAll();} else{lock.wait();}}}}public void odd(IntConsumer printNumber) throws InterruptedException {for(int i=1;i<=n;){synchronized (lock) {if(flag%4 == 2){System.out.println(i);i = i+2;flag++;lock.notifyAll();} else {lock.wait();}}}}public void even(IntConsumer printNumber) throws InterruptedException{for(int i=2;i<=n;){synchronized (lock) {if(flag%4 == 0){System.out.println(i);i = i+2;flag++;lock.notifyAll();} else {lock.wait();}}}}
}
ReentrantLock+Condition
下面为我第一遍写的错误例子
错误原因,假设先跑zero 线程,执行了 oddCondition.signalAll();后zeroCondition.await();,此时线程释放锁,而后才执行odd()线程,获取锁后oddCondition.await();,由于 .signal只能通知到已经在等待的线程,不能通知未来等待的线程,故,代码卡死了。
class ZeroEvenOdd {private int n;private ReentrantLock lock = new ReentrantLock();private Condition zeroCondition = lock.newCondition();private Condition evenCondition = lock.newCondition();private Condition oddCondition = lock.newCondition();public ZeroEvenOdd(int n) {this.n = n;}public void zero() throws InterruptedException {for(int i=1;i<=n;i++){lock.lock();System.out.println(0);if(i%2==1){oddCondition.signalAll();} else {evenCondition.signalAll();}zeroCondition.await();lock.unlock();}}public void even() throws InterruptedException {for(int i=2;i<=n;){lock.lock();evenCondition.await();System.out.println(i);i=i+2;zeroCondition.signalAll();lock.unlock();}}public void odd() throws InterruptedException {for(int i=1;i<=n;){lock.lock();oddCondition.await();System.out.println(i);i=i+2;zeroCondition.signalAll();lock.unlock();}}
修改如下
class ZeroEvenOdd {private int n;private ReentrantLock lock = new ReentrantLock();private Condition zeroCondition = lock.newCondition();private Condition evenCondition = lock.newCondition();private Condition oddCondition = lock.newCondition();private volatile int flag = 1;public ZeroEvenOdd(int n) {this.n = n;}public void zero() throws InterruptedException {for(int i=1;i<=n;){lock.lock();if(flag%4 ==1 || flag%4 == 3){System.out.println(0);flag++;i++;if(flag%4 ==0){evenCondition.signal();} else {oddCondition.signal();}} else {zeroCondition.await();}lock.unlock();}}//偶数public void even() throws InterruptedException {for(int i=2;i<=n;){lock.lock();if(flag%4 ==0){System.out.println(i);i=i+2;flag++;zeroCondition.signal();} else {evenCondition.await();}lock.unlock();}}//奇数public void odd() throws InterruptedException {for(int i=1;i<=n;){lock.lock();if(flag%4 == 2){System.out.println(i);i=i+2;flag++;zeroCondition.signal();} else {oddCondition.await();}lock.unlock();}}}
下面这个解的重点是notify 和 notifyAll 的区别
notify和notifyAll的区别
首先讲这俩区别之前先来了解两个概念。锁池EntryList和等待池WaitSet。而这两个池和Object基类的notify
锁池
假设线程A已经拥有了某个对象(不是类)的锁,而其它线程B、C想要调用这个对象的某个synchronized方法(或者代码块), 由于B、C线程在进入对象的synchronized方法(或者块)之前必须先获得该对象锁的拥有权,而恰巧该对象的锁目前正被线程A所占用,此时B、C线程就会被阻塞,进入一个地方去等待锁的释放,这个地方便是该对象的锁池
等待池
假设线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入到等待池中的线程不会去竞争该对象的锁。
notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会
分析:
当current = 1时,执行number()里面,其他线程要么为执行,要么lock.wait();在等待池里,current =3时,number()进入等待池,由于 lock.notifyAll();唤醒了其他几个线程进入锁池,除了fizz满足条件,其他线程都执行了lock.wait();重新进入等待池。
class FizzBuzz {private int n;private final Object lock;private int current;public FizzBuzz(int n) {this.n = n;current = 1;lock = new Object();}// printFizz.run() outputs "fizz".public void fizz(Runnable printFizz) throws InterruptedException {synchronized (lock) {while (current <= n) {if (current % 3 == 0 && current % 5 != 0) {current++;printFizz.run();lock.notifyAll();} else {lock.wait();}}}}// printBuzz.run() outputs "buzz".public void buzz(Runnable printBuzz) throws InterruptedException {synchronized (lock) {while (current <= n) {if (current % 5 == 0 && current % 3 != 0) {current++;printBuzz.run();lock.notifyAll();} else {lock.wait();}}}}// printFizzBuzz.run() outputs "fizzbuzz".public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {synchronized (lock) {while (current <= n) {if (current % 15 == 0) {current++;printFizzBuzz.run();lock.notifyAll();} else {lock.wait();}}}}// printNumber.accept(x) outputs "x", where x is an integer.public void number(IntConsumer printNumber) throws InterruptedException {synchronized (lock) {while (current <= n) {if (current % 3 != 0 && current % 5 != 0) {printNumber.accept(current);current++;lock.notifyAll();} else {lock.wait();}}}}
}