wait / notify / notifyAll 和 synchronized method / block

调用wait, notify, notifyAll的代码必须位于synchronized内,否则系统会抛出IllegalMonitorStateException的异常。

 

首先来了解一下关键字 synchronized

synchronized

为了解决共享资源并发访问的为题,Java内置了synchronized关键字来解决线程同步问题。先看下面这个例子,两个不同的线程同时对变量sharedResource进行操作,就是所谓的Race Condition

public class RaceCondition {
    int sharedResource = 0;

    public void startTwoThreads() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                sharedResource++;
            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                sharedResource--;
            }
        });
        t2.start();
    }
}

这段代码是有问题的。t1t2run方法中,变量sharedResource的状态可能是不稳定的。这是因为sharedResource++sharedResource--并不是[原子(Atomic)操作](http://www.liangfeizc.com/blog/article/21/),而且sharedResource不是volatile变量,它的值可能不会被及时更新,所有还存在visiblitiy的问题。为了Thread Safety,解决共享变量访问冲突的问题,java提供了synchronized关键字。加上了synchronized关键字后的代码如下所示:

public class RaceCondition {
    int sharedResource = 0;

    public synchronized void increment() {
        sharedResource++;
    }

    public synchronized void decrement() {
        sharedResource--;
    }


    public void startTwoThreads() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                increment();
            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                decrement();
            }
        });
        t2.start();
    }
}

这样当线程t1进入increment方法后就获得了整个Object的锁,t2就无法进入decrement方法。那synchronizedwait, notify又有什么关系呢?

wait & notify

waitnotify是成对使用的,可以通过生产者消费者问题来了解它们的用法。

import java.util.LinkedList;

public class ConsumerProducer {
    private LinkedList list = new LinkedList();
    private final int LIMIT = 10;
    private Object lock = new Object();

    public void produce() throws InterruptedException {
        int value = 0;

        while (true) {
            synchronized (lock) {
                while (list.size() == LIMIT) {
                    lock.wait();
                }
                list.add(value++);
                lock.notify();
            }
        }
    }

    public void consume() throws InterruptedException {
        while (true) {
            synchronized (lock) {
                while (list.size() == 0) {
                    lock.wait();
                }
                int value = list.removeFirst();
                lock.notify();
            }
        }
    }

    public static void main(String[] args) {
        final ConsumerProducer cp = new ConsumerProducer();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    cp.produce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    cp.consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2.start();
    }
}

通常情况下,当资源不满足条件,线程无法继续执行下去的时候,我们会调用wait进入wait queue,同时当前线程会释放lock,并转入sleep状态(这也是object.wait()Thread.sleep()的区别之一)。当其他线程执行notify()来唤醒进入wait状态的线程时,我们会再次查看是否满足了继续执行的条件,否则再次进入wait。

produce方法中的

while (list.size() == LIMIT) { 
    lock.wait();
}

会判断资源是否满足条件,而consume方法中的

int value = list.removeFirst();
lock.notify();

会发出一个资源变化的通知。

这样一来,调用produce的线程t1与调用consume的线程t2会有一个共享资源 -- list。如果没有synchronized关键字,会发生下面这种死锁情况。

t1执行完while (list.size() == LIMIT)后,CPU切换到t2,继续执行list.removeFirst();lock.notify();,而此时t1并没有进入wait状态,所以收不到notify发来的消息,CPU又切换回t1,执行lock.wait();,如果此时t2线程被终止,那么t1就会一直wait下去。

所以解决wait,notify,notify之间Race Condition的问题,必须使用synchronized关键字。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值