先來看看下面的程序
A高蜂、C兩個(gè)線程做number的增1罕容,B、D 兩個(gè)線程做number的減1
class MpCust {
private int number = 0;
public synchronized void increment() throws InterruptedException {
if (number != 0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"\n"+number);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"\n"+number);
this.notifyAll();
}
public static void main(String[] args) {
MpCust mpCust = new MpCust();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(300);
mpCust.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(400);
mpCust.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(300);
mpCust.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"C").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(400);
mpCust.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"D").start();
}
}
運(yùn)行結(jié)果中有值打印為2露泊,這是不正常的旅择!
因?yàn)閕ncrement()方法執(zhí)行到這里生真,number 的值不為0的話就會(huì)執(zhí)行Object.wait()方法沉噩,當(dāng)前線程會(huì)進(jìn)入等待柱蟀。
if (number != 0){
this.wait();
}
因?yàn)檫@段代碼是在多線程下執(zhí)行的,線程執(zhí)行到 this.wait();等待畜眨,蘇醒后繼續(xù)執(zhí)行。這時(shí)候如果別的線程已經(jīng)將number的值改為1康聂,那么就不會(huì)再去判斷一次number 是否等于0,就直接往下執(zhí)行number++,導(dǎo)致number的值為2撬讽。這種想象我們稱之為虛假喚醒
所以需要一種機(jī)制讓線程蘇醒之后再次判斷
number的值是否等于0。這時(shí)候java中的while循環(huán)正好排上用場(chǎng)甘苍。
使用while循環(huán)代替if條件
while(number != 0){
this.wait();
}
class MpCust {
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"\n"+number);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"\n"+number);
this.notifyAll();
}
public static void main(String[] args) {
MpCust mpCust = new MpCust();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(300);
mpCust.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(400);
mpCust.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(300);
mpCust.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"C").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(400);
mpCust.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"D").start();
}
}
完美~~