死锁:两个或两个以上的线程互相等待彼此的锁。
举个栗子:
小明和小花是朋友,假设当有朋友拥抱自己的时候,自己必须要回应对方的拥抱,这样我们就定义两个方法,一个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();
}
}
运行结果:
收到的信息为:母马吃燕麦
收到的信息为:狗吃骨头
收到的信息为:小马吃草
收到的信息为:小孩吃奶