死锁、饥饿、活锁、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();
	}
}

运行结果:

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






猜你喜欢

转载自blog.csdn.net/sinat_36265222/article/details/78356910