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线程同步
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);
}
}
}
栈为空时,pop一直等待,这本来就是期望的行为
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);就会出错
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就目前来看,确实没什么必要。
不多余,因为还有一个有并行的pop()
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);