1、生产者消费者模式使用nodify的弊端-死锁
该实例中消费者吃完了后本来是唤醒生产者生产油条 但把另一个消费者唤醒了,然后被唤醒的消费者开始吃 发现没有了就开始wait而此时生产者并没有被唤醒也在wait 就导致大家都在wait 导致死锁
下面代码为死锁情况
package ProducerAndConsumer;
/**
* 死锁了
* 生产者消费者模式使用nodify的弊端:
* 有可能会唤醒本类的线程即:
* 在多生产这多消费者的时候会出现
* 消费者唤醒消费者,生产者唤醒生产者而的情况。
*
* 吃完了后本来是唤醒生产者生产油条 但把另一个消费者唤醒了,然后被唤醒的消费者开始吃 发现没有了就开始wait
* 而此时生产者并没有被唤醒也在wait 就导致大家都在wait 导致死锁
* @author xzq
*/
public class ProducerAndConsumerTest {
public static void main(String[] args) {
ShareData data=new ShareData();
new Producer2(data,"生产者01").start();
new Producer2(data,"生产者02").start();
new Consumer2(data,"消费者01").start();
new Consumer2(data,"消费者02").start();
}
}
class ShareData {
private String name;
private int count=0;
private int number=1;
private static final int MAX=1;
//生产
public synchronized void product(String name) throws InterruptedException{
String threadName = Thread.currentThread().getName();
System.out.println(threadName+":要开始生产了……");
//如果等于最大就等待
while(count==MAX){
System.out.println(threadName+":已经达到最大,等着消费者消费……");
this.wait();
Thread.sleep(10);
}
//如果没有达到最大,就生产
this.name="第"+number+"号"+name;
count++;
number++;
System.out.println(threadName+":已经做好了"+this.name+"啦!");
/*
* 做好一个就唤醒消费者进行消费
* 注意这里的nodify不一定唤醒的是消费者
*/
this.notify();//会导致死锁
// this.notifyAll(); //不会死锁
}
//消费
public synchronized void consume() throws InterruptedException{
String threadName = Thread.currentThread().getName();
System.out.println(threadName+":要开始消费了……");
//如果没有了,就等待
while(count==0){
this.wait();
Thread.sleep(10);
}
//如果达到最大就消费
count--;
System.out.println(threadName+":吃掉了"+name);
/*
* 没有了就唤醒生产者可以继续生产了
* 但是唤醒的不一定是生产者
*
* 吃完了后本来是唤醒生产者生产油条 但把另一个消费者唤醒了,然后被唤醒的消费者开始吃 发现没有了就开始wait
* 而此时生产者并没有被唤醒也在wait 就导致大家都在wait 导致死锁
*/
this.notify();//会导致死锁
//this.notifyAll(); //不会死锁
}
}
class Producer2 extends Thread{
private ShareData data;
public Producer2(ShareData data,String name) {
super(name);
this.data=data;
}
@Override
public void run() {
try {
//每个进入的线程不停的生产,循环判断是否生产
for (; ;) {
data.product("油条");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer2 extends Thread{
private ShareData data;
public Consumer2(ShareData data,String name) {
super(name);
this.data=data;
}
@Override
public void run() {
try {
//消费10根油条
for (; ; ) {
data.consume();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
吃完了后本来是唤醒生产者生产油条 但把另一个消费者唤醒了,然后被唤醒的消费者开始吃 发现没有了就开始wait 而此时生产者并没有被唤醒也在wait 就导致大家都在wait 导致死锁
如果使用notifyAll()就不会导致死锁,可以全部唤醒,下面是运行结果
2、对run方法加上synchronized是否能达到同步效果
观察继承Thread类的时候,对run方法加上synchronized是否能达到同步效果
使用继承Thread类的方式在run方法上加synchronized达不到同步的效果,因为每个线程锁的都是自己,而这每个线程又是不同的对象,所以达不到同步效果。
package day03;
/**
* 观察继承Thread类的时候,
* 对run方法加上synchronized是否能达到同步效果
*
* 使用继承Thread类的方式在run方法上加synchronized
* 达不到同步的效果,因为每个线程锁的都是自己,而这
* 每个线程又是不同的对象,所以达不到同步效果。
* @author xzq
*/
public class WatchThread {
public static void main(String[] args) {
for (int i = 1; i <= 3; i++) {
MyThread mt = new MyThread("线程"+i);
//设置线程的名称,方便查看在执行哪个对象的run()
mt.start();
}
}
static class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public synchronized void run() {
String threadName = Thread.currentThread().getName();
/*
如果像下面synchronized (WatchThread.class)加 三个线程就会依次执行,
new 了三个MyThread对象,每个对象都有自己的run方法
*/
// synchronized (WatchThread.class) {
//循环10次,每1秒钟循环一次
for (int i = 1; i <= 10; i++) {
System.out.println(threadName+":执行第"+i+"次循环……");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//synchronized结束
// }
}
}
}
new 了三个MyThread对象,每个对象都有自己的run方法,所以达不到同步的目的,将run里的逻辑放在synchronized (WatchThread.class){ }代码块中可以解决问题
@Override
public /*synchronized*/ void run() {
String threadName = Thread.currentThread().getName();
/*
如果像下面synchronized (WatchThread.class)加 三个线程就会依次执行,
new 了三个MyThread对象,每个对象都有自己的run方法
*/
synchronized (WatchThread.class) {
//循环10次,每1秒钟循环一次
for (int i = 1; i <= 5; i++) {
System.out.println(threadName+":执行第"+i+"次循环……");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//synchronized结束
}
}
结果如下: