public class MyStack {
private List<String> list = new ArrayList<String>();

public synchronized void push(String value){
synchronized (this) {
list.add(value);
notify();
}
}
public synchronized String pop () throws InterruptedException{
synchronized (this) {
if(list.size() <= 0){
wait();
}
return list.remove(list.size() - 1);
}
}
}
Java线程同步

解决方案 »

  1.   

    当list是空,调用pop而又没有其他函数调用push,这样会一直等待
      

  2.   

    public class MyStack {
        private List<String> list = new ArrayList<String>();
         
        public synchronized void push(String value){
            synchronized (this) {
                list.add(value);
                notify();
            }
        }
        public synchronized String pop () throws InterruptedException{
            synchronized (this) {
                if(list.size() <= 0){
                    return;
                }
                return list.remove(list.size() - 1);
            }
        }
    }
      

  3.   

    我怎么感觉没什么问题呢,除了synchronized (this) 这一行是多余的。
    栈为空时,pop一直等待,这本来就是期望的行为
      

  4.   

    通常的做法是
    while(list.size() <= 0){ wait(); }
    而不是 if(list.size() <= 0){ wait(); }仔细思考了一下,用if的话可能存在如下问题,
    thread1 pop
    thread2 push
    thread3 pop
    在thread2 push完成,将thread1刚刚移出等待集,thread3抢到了锁,thread3执行完毕之后,thread1继续执行return list.remove(list.size() - 1);就会出错
      

  5.   

    谢谢回答,我也觉得synchronized (this)是多余的,方法已经被同步了,方法内有又只有一个语句块,同步似乎是有点多余。我还有一个问题,就是,pop方法是被同步的,就像你说的那样,Thread1刚刚移出等待集他还没有结束(还在pop方法里,pop被同步),请问下,这时候,Thread3可以抢到锁么,因为我觉得Thread1还没有释放锁
      

  6.   

    谢谢你的回答,你这样的话,确实可以避免长时间的等待,想再问下,你觉得synchronized (this)这个是不是多余了,因为方法已经被synchronized了
      

  7.   

    不用while, pop() 等待的时候,调用另一个类的另一个方法
    public void g() {
    final MyStack s = new MyStack();
    synchronized (s) {
    //....
    s.notify();
    }
    }
    这样就会出现问题。所以一般都用while。
    或者这样 class MyStack { private List<String> list = new ArrayList<String>();
    private final Object lock = new Object(); public synchronized void push(String value) {
    synchronized (lock) {
    list.add(value);
    notify();
    }
    } public synchronized String pop() throws InterruptedException {
    synchronized (lock) {
    if (list.size() <= 0) {
    wait();
    }
    return list.remove(list.size() - 1);
    }
    }
    }
    方法上的synchronized就目前来看,确实没什么必要。
      

  8.   

    谢谢你的回答,你这样的话,确实可以避免长时间的等待,想再问下,你觉得synchronized (this)这个是不是多余了,因为方法已经被synchronized了
    不多余,因为还有一个有并行的pop()
      

  9.   

    谢谢回答,我也觉得synchronized (this)是多余的,方法已经被同步了,方法内有又只有一个语句块,同步似乎是有点多余。我还有一个问题,就是,pop方法是被同步的,就像你说的那样,Thread1刚刚移出等待集他还没有结束(还在pop方法里,pop被同步),请问下,这时候,Thread3可以抢到锁么,因为我觉得Thread1还没有释放锁
    Thread1在调用wait的时候,就已经放弃了锁,并且进入等待集。在Thread2 notify并且结束之后,Thread1和Thread3一起竞争锁。JDK对wait方法的说明:此方法导致当前线程(称之为 T)将其自身放置在对象的等待集中,然后放弃此对象上的所有同步要求。出于线程调度目的,在发生以下四种情况之一前,线程 T 被禁用,且处于休眠状态:  其他某个线程调用此对象的 notify 方法,并且线程 T 碰巧被任选为被唤醒的线程。 
      其他某个线程调用此对象的 notifyAll 方法。 
      ....然后,从对象的等待集中删除线程 T,并重新进行线程调度。然后,该线程以常规方式与其他线程竞争,以获得在该对象上同步的权利;一旦获得对该对象的控制权,该对象上的所有其同步声明都将被恢复到以前的状态,这就是调用 wait 方法时的情况。然后,线程 T 从 wait 方法的调用中返回。所以,从 wait 方法返回时,该对象和线程 T 的同步状态与调用 wait 方法时的情况完全相同。 ...换句话说,等待应总是发生在循环中,如下面的示例: while (<condition does not hold>)
    obj.wait(timeout);