开门见山直说:
在java多线程情况下,当线程被wait后,又通过notify方法唤醒时,在if情况下被唤醒,程序从哪里wait的就从哪里继续执行,在while的情况下被唤醒,程序依旧会从哪里wait的继续往下执行,但是,在执行之前会先对循环条件进行判断,满足条件继续wait,不满足条件继续执行。而if语句不会再对条件进行判断,直接继续执行。所以在多线程情况下,可能会有多个线程再对资源进行修改,当被wait的线程被唤醒时,有其他线程已经将资源进行修改过了,导致 while的条件发生变化(不再满足)。而刚唤醒的线程根本不知道,所以导致异常。
实例:
class SynStack
{
private char[] data = new char[6];
private int cnt = 0; //表示数组有效元素的个数
public synchronized void push(char ch)
{
if (cnt >= data.length)
{
try
{
System.out.println("生产线程"+Thread.currentThread().getName()+"准备休眠");
this.wait();
System.out.println("生产线程"+Thread.currentThread().getName()+"休眠结束了");
}
catch (Exception e)
{
e.printStackTrace();
}
}
this.notify();
data[cnt] = ch;
++cnt;
System.out.printf("生产线程"+Thread.currentThread().getName()+"正在生产第%d个产品,该产品是: %c\n", cnt, ch);
}
public synchronized char pop()
{
char ch;
if (cnt <= 0)
{
try
{
System.out.println("消费线程"+Thread.currentThread().getName()+"准备休眠");
this.wait();
System.out.println("消费线程"+Thread.currentThread().getName()+"休眠结束了");
}
catch (Exception e)
{
e.printStackTrace();
}
}
this.notify();
ch = data[cnt-1];
System.out.printf("消费线程"+Thread.currentThread().getName()+"正在消费第%d个产品,该产品是: %c\n", cnt, ch);
--cnt;
return ch;
}
}
class Producer implements Runnable
{
private SynStack ss = null;
public Producer(SynStack ss)
{
this.ss = ss;
}
public void run()
{
char ch;
for (int i=0; i<10; ++i)
{
ch = (char)('a'+i);
ss.push(ch);
}
}
}
class Consumer implements Runnable
{
private SynStack ss = null;
public Consumer(SynStack ss)
{
this.ss = ss;
}
public void run()
{
for (int i=0; i<10; ++i)
{
ss.pop();
}
}
}
public class TestPC2 {
public static void main(String[] args)
{
SynStack ss = new SynStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
Thread t1 = new Thread(p);
t1.setName("1号");
t1.start();
/*Thread t2 = new Thread(p);
t2.setName("2号");
t2.start();*/
Thread t6 = new Thread(c);
t6.setName("3号");
t6.start();
Thread t7 = new Thread(c);
t7.setName("4号");
t7.start();
}
}
补充:
在思考上面的问题的时候,发现了一个很好玩的东西,就是通过new ArrayList<String>()来声明一个新对象的时候,其实这个对象不是空的,里面是有一个值的也就是anyString,但是这个值是不计数的,我们通过size()获取的元素大小是直接获取的ArrayList的size成员变量;
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;