死锁

来源:互联网 发布:知乎 王宝强 马蓉 编辑:程序博客网 时间:2024/06/18 13:08

线程之间可以通过使用synchronized 关键字或者其他方式加锁来防止其他的任务在互斥还没有释放时就访问临界资源。我们知道线程会变成阻塞状态,所以在某种状态下就可能出现以下的状况:某个任务在等待另一个任务,而后者又在等待其他的任务,这样一直下去,直到这个链条上的任务又在等待第一个任务执行释放锁。这样所有的任务将会出现互相等待的情况,并没有哪个线程能够正常执行,这就被称为死锁。

死锁发生同时满足的四个条件:
      只要这四个条件同时满足时,就会发生死锁。

1.互斥条件。任务使用的资源必须有一个是不能共享的。
2.至少有一个任务必须持有一个被别的任务持有的资源。
3.资源不能被抢占,任务必须把资源当作普通事件。
4.必须有循环等待,这时一个任务等待其他任务所持有的资源,后者又在等待另一个任务持有的资源,这样一直下去,直到有一个任务在等待第一个任务所持有的资源,使得大家都被锁住。

      接下来以生产者为例,只不过同时有两个生产者与两个消费者,在生产完成的时候才可以进行消费,消费完成后再进行生产,并且每次只有一个生产者或消费者执行任务。为了演示死锁状态这里就以死锁状态进行分析:

class Resource{    private String name;    private int count = 1;    private boolean flag = false;    public synchronized void set(String name){        while(true){            while(flag){                       try {                    wait();                } catch (InterruptedException e) {                    System.out.println("生产完成");                }            }            this.name = name + count;            count++;            System.out.println(Thread.currentThread().getName()+ "...生产者...." + this.name);            flag = true;            notify();                   }    }    public synchronized void out(){        while(true){            while(!flag){                try {                    wait();                } catch (InterruptedException e) {                    System.out.println("消费完成");                }            }            System.out.println(Thread.currentThread().getName()+"...消费者...." + this.name);            flag = false;            notify();        }    }}class Producer implements Runnable{    Resource rs;    Producer(Resource rs){        this.rs = rs;    }    public void run(){        rs.set("烤鸭");    }}class Consumer implements Runnable{    Resource rs;    Consumer(Resource rs){        this.rs = rs;    }    public void run(){        rs.out();    }}public class Restaurant {    public static void main(String args []) throws InterruptedException {        Resource rs = new Resource();        Producer producer = new Producer(rs);               Consumer consumer = new Consumer(rs);        new Thread(producer).start();        new Thread(producer).start();        new Thread(consumer).start();        new Thread(consumer).start();    }}

      上面这个案例我们创建了两个生产者线程与两个消费者线程,每次其中一个生产者执行完生产任务的时候,如果另一个生产者线程得到执行权那么它将会被挂起,因为每次只能生产一个资源,在生产后就由消费者线程执任务去消费它;同理如果其中一个消费者线程执行完消费任务释放锁后另一个消费者得到了执行权那么它也会被挂起。无论是生产者将要完成任务还是消费者将要完成任务它都会改变标识位并唤醒其中一个挂起的线程(为了演示死锁现象并不是唤醒所有挂起的线程)。
      我么假设生产者线程1 和 2,消费者线程3 和 4,上面这个案例在某个时刻会处于一种情况:生产者1 执行完任务被生产者2 获得执行权,此时生产者2 判断标识后被挂起,消费者3 获得执行权,消费者3 执行完任务后被消费者4 获得执行权,经过判断标识后被挂起,此时有两个线程被挂起2 和 4,释放锁后被消费者1 获得执行权, 消费者1 获得执行权后完成任务后唤醒了生产者2,接下来生产者1 和 2 先后获得执行权,由于消费者没有执行任务所以1 和 2 都将被挂起,这时被挂起的线程有1,2,4,被挂起后消费者3 获得执行权,执行完任务唤醒了4 ,接下来消费者3 和 4又前后获得执行权,由于两个生产者线程已经处于被挂起的状态,它们无消费者任务执行因此也被先后挂起,这样在某一时刻所有的生产者与消费者线程都会被挂起,它们的任务将不会被执行,这时就进入了死锁状态。
      如果想将上面这个案例改成正确的我们只要将notify() 改为:notifyAll() 即可。这里就不进行讲述了,有兴趣的可以自己尝试着分析一下。

原创粉丝点击