总结:
- 同步锁的位置(synchronized的位置):方法、方法体、静态代码块都可以
- 同步锁 锁住的对象:多个线程需要共用哪个类的资源,哪个类就是对象
- wait,notify,notifyAll属于Object类,也就是每个对象都有wait,notify,notifyAll 的功能,但是使用 obj.wait,obj.notify,obj.notifyAll 的必须是同一个对象。因为多个线程只有使用相同的一个对象的时候,多线程之间才有互斥效果
- wait,notify,notifyAll必须放在synchronized包裹的代码中执行,且只能被同步监听锁对象来调用
三个类:Producer、Consumer、Goods。生产者生产goods,消费者消耗goods,goods即是他们要竞争的公共资源。
第一种写法,写在方法上:
Producer:
public class Producer extends Thread{
private Goods goods;
public Producer(Goods goods){
this.goods=goods;
}
public void run(){
while (true){
goods.product();
}
}
}
Cunsumer:
public class Consumer extends Thread{
private Goods goods;
public Consumer(Goods goods){
this.goods=goods;
}
public void run(){
while(true) {
goods.consume();
}
}
}
Goods:
public class Goods {
private int good;
public Goods(int good) {
this.good = good;
}
public synchronized void consume() { //消费
while (good <= 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
--good;
System.out.println(Thread.currentThread().getName()+"消耗了货物,还剩" + good);
notifyAll();
}
public synchronized void product() { //生产
while (good > 10) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
++good;
System.out.println(Thread.currentThread().getName()+"生产了货物,还剩" + good);
notifyAll();
}
}
Test:
public class Test {
public static void main(String[] args) {
Goods goods=new Goods(1);
new Consumer(goods).start();
new Consumer(goods).start();
new Producer(goods).start();
new Producer(goods).start();
}
}
第二种写法,写在方法体里面:
生产者Producer:
public class Producer extends Thread{
private Goods goods;
public Producer(Goods goods){
this.goods=goods;
}
public void run(){
while (true){
synchronized (goods){
int good;
while ((good=goods.getGood()) > 10) {
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
++good;
goods.setGood(good);
System.out.println(Thread.currentThread().getName()+"生产了货物,还剩" + good);
goods.notifyAll();
}
}
}
}
消费者Consumer:
public class Consumer extends Thread{
private Goods goods;
public Consumer(Goods goods){
this.goods=goods;
}
public void run(){
while(true) {
synchronized (goods){
int good;
while ((good=goods.getGood()) <= 0) {
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
--good;
goods.setGood(good);
System.out.println(Thread.currentThread().getName()+"消耗了货物,还剩" + good);
goods.notifyAll();
}
}
}
}
他们竞争的资源Goods:
public class Goods {
private int good;
public Goods(int good) {
this.good = good;
}
public int getGood() {
return good;
}
public void setGood(int good) {
this.good = good;
}
}
测试类Test:
public class Test {
public static void main(String[] args) {
Goods goods=new Goods(1);
new Consumer(goods).start();
new Consumer(goods).start();
new Producer(goods).start();
new Producer(goods).start();
}
}
注意:
第二种方法,在wait和notifyAll前面加了goods,如果不加会报错 IllegalMonitorStateException
- 源码里面这样解释这个异常:
而第一种方法不加不会报错,查看了一下第一种方法反编译的class文件,实际上调用wait和notifyAll的代码是这样的:
为什么呢:
- wait()、notify()和notifyAll()都是定义在Object类中,所以任意对象都有这三种方法,但是这三种方法只能被同步监听锁对象来调用
- Java会在编译的时候给类里面的成员变量和方法,默认加上this。这样写两种方法wait和notifyAll的对象才是goods,如果第二种方法不加,实际上是调用的生产者和消费者的wait和notifyAll方法。对象都不一样了,Consumer.wait怎么能被Producer.notifyAll唤醒呢,再加上这里的同步监听锁对象是goods,那只能使用goods的wait和notifiAll方法,所以如果不加goods就会报错。