死锁:过多的同步容易造成死锁
使用同一份资源谁都不放手就锁住了
模拟死锁
package study;
/**
* 模拟死锁
* @author http://blog.csdn.net/thewaiting/
*
*/
public class ThreadDome {
public static void main(String[] args) {
Object goods = new Object();
Object money = new Object();
Test t1 = new Test(goods, money);
Test2 t2 = new Test2(goods, money);
Thread proxy = new Thread(t1);
Thread proxy2 = new Thread(t2);
proxy.start();
proxy2.start();
}
}
class Test implements Runnable {
Object goods = new Object();
Object money = new Object();
public Test(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
while (true) {
test();
}
}
private void test() {
synchronized (goods) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (money) {
}
}
System.out.println("给钱");
}
}
class Test2 implements Runnable {
Object goods;
Object money;
public Test2(Object goods, Object money) {
this.goods = goods;
this.money = money;
}
@Override
public void run() {
while (true) {
test();
}
}
private void test() {
synchronized (money) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (goods) {
}
}
System.out.println("给货");
}
}
生产者消费者(也成有限缓冲问题)模式解决死锁
先生产 后消费
生产者在缓冲区满时休眠(要么干脆放弃数据)等到下次消费者消耗缓冲区的数据的时候,生产者才能被唤醒开始往缓冲区添加数据。
消费者在缓冲区空时休眠,等到生产者往缓冲区添加数据再唤醒消费者。
通常的方法:信号灯法,管程等
如果解决方法不够完善则容易出现死锁情况
出现死锁时连个线程都会陷入休眠,等待对方唤醒自己
首先模拟一下
package study;
//测试
public class Text {
public static void main(String[] args) {
//共同的资源
ThreadDome2 t = new ThreadDome2();
//多线程 同时使用资源t
Production p = new Production(t);
Consume c = new Consume(t);
new Thread(p).start();
new Thread(c).start();
}
}
运行结果
生产 生产 生产 生产 生产 生产 生产 生产 生产 生产 生产 生产 生产 生产 生产 生产 生产 生产 生产 生产
package study;
/**
*
* @author http://blog.csdn.net/thewaiting/
*
*/
public class ThreadDome2 {
private String resource;
public void production(String resource) {
this.resource =resource;
}
public void consume() {
System.out.print(resource+" ");
}
}
/**
* 生产者
* @author http://blog.csdn.net/thewaiting/
*
*/
class Production implements Runnable{
private ThreadDome2 t;//资源
public Production(ThreadDome2 t) {
super();
this.t = t;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (0==i%2) {
t.production("生产/被2整除");
}else {
t.production("生产");
}
}
}
}
/**
* 消费者
* @author http://blog.csdn.net/thewaiting/
*
*/
class Consume implements Runnable{
private ThreadDome2 t;
public Consume(ThreadDome2 t) {
super();
this.t = t;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
t.consume();
}
}
}
信号灯法
唤醒:
notify() 唤醒在此对象监视器上等待的单个线程
notifyAll() 唤醒在此对象监视器上等待的所有线程
等待:
wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。等待会释放锁sleep()不是释放锁*
以上方法要与同步一起使用,没有同步则无法等待
修改ThreadDome2 代码
package study;
/**
* 使用生产者消费者模式
* 信号灯法
* @author http://blog.csdn.net/thewaiting/
*
*/
public class ThreadDome2 {
private String resource;
//信号灯
//当flag为true时生产者生产,消费者等待,生产完成后通知消费者
//当flag为false消费者时生产,生产者等待,消费完成后通知生产者
private boolean flag = true;
public synchronized void production(String resource) throws InterruptedException {
if (!flag) {//生产者等待
this.wait();
}
//开始生产
Thread.sleep(500);//模拟生产时间 放大错误的概率
//生产完毕
this.resource =resource;
//通知消费
this.notify();
//生产者停下
this.flag = false;
}
public synchronized void consume() throws InterruptedException {
if (flag) {//消费者等待
this.wait();
}
//开始消费
Thread.sleep(200);//模拟消费时间 放大错误的概率
System.out.print(resource+" ");
//消费完毕
//通知生产者
this.notify();
//消费停止
this.flag = true;
}
}
运行结果
生产/被2整除 生产 生产/被2整除 生产 生产/被2整除 生产 生产/被2整除 生产 生产/被2整除 生产 生产/被2整除 生产 生产/被2整除 生产 生产/被2整除 生产 生产/被2整除 生产 生产/被2整除 生产