死锁、饥饿、活锁、wait()、notifyAll()、notify()

死锁:两个或两个以上的线程互相等待彼此的锁。

举个栗子:

小明和小花是朋友,假设当有朋友拥抱自己的时候,自己必须要回应对方的拥抱,这样我们就定义两个方法,一个bow()拥抱方法,一个backBrow()回应拥抱方法。我们在bow方法中调用对方的backbow()方法来响应对方,具体代码如下:

package com.zrh.thread;

/**
 * 死锁例子
 * @author ljh
 *
 */
public class Deadlock {

	static class Friend{
		private String name;

		public Friend(String name) {
		
			this.name = name;
		}
		
		public String getName(){
			return this.name;
		}
		
		public synchronized void brow(Friend bower){
			System.out.format("%s:%s"+" has bowed to me!%n", this.name,bower.getName());
			bower.backBow(this);
		}
		
		public synchronized void backBow(Friend bower){
			System.out.format("%s:%s"+" has bowed back to me !%n", this.name,bower.getName());
		}
	}
	
	public static void main(String[] args) {
		final Friend xiaoHua = new Friend("xiaoHua");
		final Friend xiaoMing = new Friend("xiaoMing");
		
		new Thread(new Runnable() {			
			public void run() {
				
				xiaoHua.brow(xiaoMing);
			}
		}).start();
		
		new Thread(new Runnable() {
			
			public void run() {
				xiaoMing.brow(xiaoHua);
			}
		}).start();
	}
}

运行结果:

xiaoHua:xiaoMing has bowed to me!
xiaoMing:xiaoHua has bowed to me!
死锁中……
结果分析如下:


饥饿:线程不能正常的访问共享资源且不能正常的执行。如当有少数的线程暂用共享资源的时间太长,导致其他需要访问共享资源的线程经常被阻塞就会出现饥饿的情况。

活锁:线程不会被阻塞,但是无法继续执行,通常是线程间的相互响应导致的,类似行人在过道上的相互谦让,但两人总是向同一个方位移动,导致虽然互相谦让了,但还是无法顺利的通过,双发僵持不下,无法继续行走。对于活锁常见的解决方法是:采用使用先到先得,或者设置重试上限。

避免死锁和饥饿的设计技巧:使用wait

	public synchronized void doSomeThing(){
		while (flage) {
			try {
				wait();
			} catch (InterruptedException e) {
			
			}
		}
		//条件成立,开始工作
	}
注意:wait往往会搭配循环使用,避免在受notifyAll或中断的影响出现flage状态并没有变,线程却被唤醒的情况。

wait()、notifyAll()、notify():假设一个线程在获得A对象的锁的时候调用wait()时,将会释放锁并挂起。这时另外一个线程获得到了A对象的锁,并且执行完毕后调用了notifyAll()通知唤醒所有在等待A对象锁的线程去竞争A对象的锁。或者调用notify(),这样就只会唤醒一个线程,不能控制具体唤醒哪一个线程,适用于 当我们处理许多相似的任务且不关心哪一个线程被唤醒的时候。(注意获得锁的线程,在执行完后一定要调用notifyAll()或notify(),否则就算获得锁的线程释放了锁,调用wait()方法等待该锁的线程也不会去竞争锁)

举个栗子:我们实现一个简单的生产者与消费者的场景.

消息管道类:

public class MeassgePipe {

	private String message;//信息从生产者发送给消费者
	private boolean empty = true;//标志位是否有信息
	
	public synchronized String take(){
		//如果当前无可以获取的消息就将线程挂起释放锁
		while (empty){
				try {
					wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}	
		}
		//有信息可以取的时候,就将信息取走,并重置标志位
		empty = true;	
		//当标识位状态为空为真的时候,通知生产者生成信息
		notifyAll();
		return message;
	}
	
	public synchronized void put(String message){
		while (!empty) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//生产信息,并修改状态
		empty = false;	
		this.message = message;
		notifyAll();
	}
}
生产者:

public class Producer implements Runnable{

	private MeassgePipe pipe;
	
	public Producer(MeassgePipe pipe) {
		this.pipe = pipe;
	}

	public void run() {
		  String[] importantInfo = {"母马吃燕麦","狗吃骨头","小马吃草","小孩吃奶"};
		Random random = new Random();
		for (int i = 0; i < importantInfo.length; i++) {
			pipe.put(importantInfo[i]);
			try {
				Thread.sleep(random.nextInt(5000));
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
		pipe.put("生产结束!");
	}

}
消费者:

package com.zrh.thread.produce_consumer;

import java.util.Random;

public class Consumer implements Runnable{

	MeassgePipe pipe;
	
	public Consumer(MeassgePipe pipe) {
		super();
		this.pipe = pipe;
	}

	public void run() {
		Random random = new Random();
		for (String message = pipe.take();!message.equals("生产结束!"); message = pipe.take()) {
			System.out.format("收到的信息为:%s%n",message);
		}
		try {
			Thread.sleep(random.nextInt(50000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
测试:

public class SimpleProducerConsumer {

	public static void main(String[] args) {
		MeassgePipe pipe = new MeassgePipe();
		new Thread(new Consumer(pipe)).start();
		new Thread(new Producer(pipe)).start();
	}
}

运行结果:

收到的信息为:母马吃燕麦
收到的信息为:狗吃骨头
收到的信息为:小马吃草
收到的信息为:小孩吃奶






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Java 中,notifynotifyAll 都是用来唤醒等待中的线程的方法。 notify 方法会随机唤醒一个等待中的线程,而 notifyAll 方法则会唤醒所有等待中的线程notify 方法通常用于在多线程中共享资源的情况下,当一个线程修改了共享资源之后,通知其他等待中的线程可以重新获取资源并继续执行。但是,如果有多个线程在等待同一个资源,而只有一个线程被唤醒,那么其他线程仍然会继续等待,因此需要使用 notifyAll 方法来唤醒所有等待中的线程。 需要注意的是,notifynotifyAll 方法必须在同步代码块中使用,即在 synchronized 代码块中调用这两个方法。否则会抛出 IllegalMonitorStateException 异常。另外,notifynotifyAll 方法的使用也要谨慎,如果使用不当可能会导致死锁活锁问题。 ### 回答2: 在Java中,多线程notifynotifyAll都是用于线程间的通信。它们都属于Object类的方法,用于唤醒等待在该对象上的线程notify方法用于唤醒在该对象上等待的某个线程。当调用notify时,系统将从该对象的等待池中选择一个线程唤醒,被唤醒的线程将进入就绪状态,等待系统调度执行。 notifyAll方法用于唤醒在该对象上等待的所有线程。当调用notifyAll时,系统将唤醒该对象上的所有线程,被唤醒的线程将进入就绪状态,等待系统调度执行。 需要注意的是,notifynotifyAll只能在同步代码块或同步方法中调用,否则会抛出IllegalMonitorStateException异常。这是因为线程在调用notifynotifyAll时必须先获得该对象的锁,才能进行通知操作。 此外,使用notifynotifyAll时需要注意以下几点: 1. 调用notifynotifyAll后,并不会立即释放锁,而是等待同步代码块或同步方法执行完毕后才会释放锁。 2. notify只能唤醒一个线程,选择唤醒的线程是不确定的,而notifyAll会唤醒所有等待的线程。 3. 使用notifynotifyAll时应该慎重,避免因过度唤醒线程导致性能下降或死锁问题的发生。 总之,通过notifynotifyAll方法,可以实现多个线程之间的协调和通信,实现线程间的同步操作。但是如果使用不当,可能会导致线程安全性和性能问题,因此需要合理地进行调度和使用。 ### 回答3: 在Java中,notifynotifyAll是两种不同的线程通信机制。 notifynotify方法用于唤醒在对象上等待的单个线程。当某个线程调用某个对象的notify方法时,它会唤醒在该对象上等待的单个线程。如果有多个线程等待该对象,但只有一个线程能被唤醒。选择哪个线程被唤醒是不确定的,取决于操作系统的调度机制。 notifyAll:notifyAll方法用于唤醒在对象上等待的所有线程。当某个线程调用某个对象的notifyAll方法时,它会唤醒在该对象上等待的所有线程。这些线程会竞争锁资源,只有一个线程能够获得该对象的锁,并继续执行。 在使用多线程的场景中,notifynotifyAll方法通常与wait方法配合使用,实现线程间的通信和协调。wait方法用于使线程等待,并释放该对象的锁,进入该对象的等待池。当其他线程调用notifynotifyAll方法时,等待线程才有可能被唤醒,重新进入该对象的锁池,继续执行。 需要注意的是,notifynotifyAll方法必须在获得相关对象的锁之后调用,否则会抛出IllegalMonitorStateException异常。另外,线程调用notify方法只能唤醒一个等待线程,而调用notifyAll方法能唤醒所有等待线程,但哪个线程能够获得锁资源是不确定的。 因此,在进行线程间通信和协调时,根据具体的需求选择使用notify还是notifyAll方法,并确保在正确的时机、正确的对象上调用这些方法,以实现线程间的正确通信和顺序控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值