2 yz454170989 yz454170989 于 2015.07.07 09:36 提问

java关于线程同步的问题

package 线程.TestTeread_5;

/*
push和pop增加减少数组元素,
我的问题是:
为什么去掉push和pop的synchronized修饰关键词时,会报如下错误
异常:
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at 线程.TestTeread_5.SynStack.push(TestTeread_5.java:19)
at 线程.TestTeread_5.Producer.run(TestTeread_5.java:57)
at java.lang.Thread.run(Unknown Source)

*/

class SynStack {//同步栈
private char [] data = new char [6];

private int cnt = 0;//cnt表示的是数组有效元素的个数

public synchronized void push(char val){
    while(cnt == data.length)
    {
        try
        {   
            this.wait();
            }
        catch(Exception e)
        {}

    }
    this.notify();
    data[cnt] = val;
    System.out.println("生产第"+cnt+"个产品,产品名为"+data[cnt]);
    cnt ++;
}
public synchronized char pop(){

    while(cnt == 0)
    {
        try
        {   
            this.wait();//暂停当前线程,转去执行另一个线程
            }
        catch(Exception e)
        {}
    }
    this.notify();
    char Key = data[cnt-1];
    System.out.println("消费第"+cnt+"个产品,产品名为"+Key);
    cnt --;
    return Key;
}

}

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<20; 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()
{

    //ss.pop(); 
    for(int i=0; i<20; i++)
    {
        ss.pop();
    }

}

}
public class TestTeread_5 {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    SynStack ss =new SynStack();
    Producer pp = new Producer(ss);
    Consumer cc = new Consumer(ss);

    Thread t1 = new Thread(pp);
    t1.start();
    Thread t2 = new Thread(cc);
    t2.start();
}

}

5个回答

lingfeiwen
lingfeiwen   2015.07.07 14:16
已采纳

楼主写的这个小例子是经典的线程同步问题,它有个名字,叫“生产者与消费者”。使用多线程的一些企业在笔试面试的时候经常会设计到,对于多线程处理,生产者与消费者只能说是HelloWorld级别的例子。

说说楼主提出的问题。
楼主说为什么去掉synchronized会出问题。楼主是启动了两个线程,两个线程通知操作同一个对象SynStack ss =new SynStack(); 说的准确点,在楼主代码例子中操作的是同一个对象ss的同一个属性char [] data 这个数组。这样就会出现多线程同步的问题。试想一下,还没开始生产,数组里面没数据,你就从数组里面拿数组就会出问题。

在楼主的例子里面,还好只有一个生产者线程,一个消费者线程,所以notify的时候基本上不会出问题,当前线程在运行,notify时,当然是notify另一个线程。如果有两个生产者和两个消费者,这时候就会出问题的,notify是随机唤醒正在wait的线程,你都不知道到底哪个线程会被唤醒。

最后看到在其他网友回答中,楼主追问了一个wait中的线程被notify了之后,是从方法头开始执行还是接着wait之后执行。答案是接着wait之后执行。所以为什么建议对wait语句使用while包起来,表示唤醒之后再次去检查下wait的条件,满足的话继续wait,为什么说建议这么做呢?因为能使wait的线程醒过来的方式不止是去notify,一些异常情况也能使线程醒过来。

bdmh
bdmh   Ds   Rxr 2015.07.07 09:49

就是数据冲突了,一个出,一个进,可能已经没有元素了,但是你另一个线程中还在访问,导致出错,或者两个线程读写发生冲突出错

yz454170989
yz454170989 如果冲突,我个人觉得应该会执行一段时间在出错,但是运行直接报错
2 年多之前 回复
yz454170989
yz454170989 麻烦详细点,一个方法wait()后,再被唤醒,这个方法是从头开始,还是接着执行wait()后面的
2 年多之前 回复
danielinbiti
danielinbiti   Ds   Rxr 2015.07.07 13:30
 wait,notify执行必须拥有对象的锁,这里是this,如果方法不加同步synchronized,只要连续执行this.wait或者this.notify中任何2次,都会报错,因为没有对象锁。
wangpeng322
wangpeng322   2015.07.07 17:11

因为notify()必须是在同步方法或者同步块中被使用啊,亲

xionglangs
xionglangs   Rxr 2015.07.08 10:05
Csdn user default icon
上传中...
上传图片
插入图片