相关博客:多线程 之 线程同步
通过生产者消费者模拟线程死锁- 产品池
public class ProductPool {
private int threshold = 1; //仓库中只能存一个产品
private LinkedList pool = new LinkedList();//放产品
//同一时刻只能有一个人生产
public synchronized void add(Integer item) {
String name = Thread.currentThread().getName();
while (pool.size() >= threshold) {
try {
System.out.println(name+".wait()");
this.wait(); // 在等待之前不发送通知 //----------- ②
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pool.add(item);
System.out.println(name +"生产后总数量:"+pool.size());
System.out.println(name+"调用notify()发送了通知");//在notify之前打印,说明已经出队列了
notify();//增加完商品之后,通知别的线程可以生产or消费了 //------- ①
}
// 同一时刻只能有一个人消费
public synchronized void remove() {
String name = Thread.currentThread().getName();
while (pool.isEmpty()) {
try {
System.out.println(name+".wait()");
this.wait();//如果仓库中没有产品,线程就等着 //----------- ②
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pool.removeFirst();
System.out.println(name+"消费后总数量:"+pool.size());
System.out.println(name+"调用notify()发送了通知");//在notify之前打印,说明已经出队列了
notify();//消费完商品之后,通知别的线程可以生产or消费了 //------- ①
}
}
- 生产者
public class Producer extends Thread {
private static int index = 0;
private ProductPool pool;
public Producer( ProductPool pool) {
this.pool = pool;
}
@Override
public void run() {
while (true) {//在产品池中不断的生产
pool.add(index++);
}
}
}
- 消费者
public class Consumer extends Thread {
private ProductPool pool;
public Consumer( ProductPool pool) {
this.pool = pool;
}
@Override
public void run() {
while (true) {//在产品池中不断的消费
pool.remove();
}
}
}
- 测试代码
public class DemoTest {
public static void main(String[] args) {//一个生产者,两个消费者
ProductPool pool = new ProductPool();
Producer p1 = new Producer(pool);
p1.setName("p1");
Consumer c1 = new Consumer(pool);
c1.setName("c1");
Consumer c2 = new Consumer(pool);
c2.setName("c2");
p1.start();
c1.start();
c2.start();
}
}
某次运行程序,结果如下:
- notify:通知一个线程
- notifyAll:通知线程等待队列中的所有线程 不管是notify()还是notifyAll()它们都只是发出通知,不会释放锁控制权。
解决方案:修改产品池 编号①处的代码为notifyAll();
修改之后产品池代码如下:
public class ProductPool {
private int threshold = 1; //仓库中只能存一个产品
private LinkedList pool = new LinkedList();//放产品
//同一时刻只能有一个人生产
public synchronized void add(Integer item) {
String name = Thread.currentThread().getName();
while (pool.size() >= threshold) {
try {
System.out.println(name + ".wait()");
this.wait(); // 在等待之前不发送通知
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pool.add(item);
System.out.println(name + "生产后总数量:" + pool.size());
System.out.println(name + "调用notify()发送了通知");//在notify之前打印,说明已经出队列了
notifyAll();//增加完商品之后,通知别的线程可以生产or消费了
}
// 同一时刻只能有一个人消费
public synchronized void remove() {
String name = Thread.currentThread().getName();
while (pool.isEmpty()) {
try {
System.out.println(name + ".wait()");
this.wait();//如果仓库中没有产品,线程就等着
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pool.removeFirst();
System.out.println(name + "消费后总数量:" + pool.size());
System.out.println(name + "调用notify()发送了通知");//在notify之前打印,说明已经出队列了
notifyAll();//消费完商品之后,通知别的线程可以生产or消费了
}
}
解决方案:限时等待: wait(1000)
修改编号 ②处的代码,让等待队列中的线程不再是一直等,只需要等待具体的时间就行了。 修改之后,产品池代码如下所示:
public class ProductPool {
private int threshold = 1; //仓库中只能存一个产品
private LinkedList pool = new LinkedList();//放产品
//同一时刻只能有一个人生产
public synchronized void add(Integer item) {
String name = Thread.currentThread().getName();
while (pool.size() >= threshold) {
try {
System.out.println(name+".wait()");
this.wait(300); // 在等待之前不发送通知
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pool.add(item);
System.out.println(name +"生产后总数量:"+pool.size());
System.out.println(name+"调用notify()发送了通知");//在notify之前打印,说明已经出队列了
notify();//增加完商品之后,通知别的线程可以生产or消费了
}
// 同一时刻只能有一个人消费
public synchronized void remove() {
String name = Thread.currentThread().getName();
while (pool.isEmpty()) {
try {
System.out.println(name+".wait()");
this.wait(300);//如果仓库中没有产品,线程就等着
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Integer res = pool.removeFirst();
System.out.println(name+"消费后总数量:"+pool.size());
System.out.println(name+"调用notify()发送了通知");//在notify之前打印,说明已经出队列了
notify();//消费完商品之后,通知别的线程可以生产or消费了
}
}
练习题:100只蜜蜂,每只蜜蜂每次生产数量蜂蜜量为1。2只熊消费蜂蜜,每只熊都会在蜜罐中的蜂蜜满20时会将蜂蜜吃掉。
知识点:wait()、notify()2个方法 wait()方法让线程进入线程等待队列当中,它的2个重要特点是:1、失去cpu抢占权。2、失去线程对象的监控权,此时没有锁可以锁住线程。与线程的sleep()方法的区别:sleep()方法仅仅只是失去cpu的抢占权,并不会失去对象的监控权。 notify()方法,唤醒在线程等待队列中的线程,注意线程是不能自我唤醒的,线程在运行当中,就没有在线程等待队列当中就没有所谓的唤醒,只有进入线程等待队列,才能被唤醒,也只能是被其它排除自己的线程所唤醒。
练习题:40个员工,一个篮子装有100个馒头,员工吃馒头并发执行,每个工人最少吃1个最多吃3个馒头,问在这种情况下怎么分配。