JAVA线程(死锁)

一,哲学家吃饭问题
1965年,Dijkstra提出并解决了一个他称之为哲学家就餐的同步问题。
哲学家的生活就是思考和吃饭,即思考,饿了就餐,再思考,循环往复。要求是:每一个哲学家只有在拿到位于他左右的筷子后,才能够就餐。
这里写图片描述

当五个哲学家都饥饿时,都拿着一支筷子,这样就可能五个哲学家都用不上餐。这就是死锁

产生死锁的条件:

1.有至少一个资源不能共享
2.至少有一个任务必须持有一个资源并且等待获取另一个被别的任务持有的资源
3.资源不能任务抢占
4.必须有循环等待

只要打破其中一个条件就不会产生死锁,通常是打破第4个条件

二,生产者消费者问题
生产者-消费者问题是一个经典的进程同步问题。在同一个进程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区(下面代码中的SynStack)中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。

实例代码:

public class ProduceConsumerTest {
    public static void main(String[] args) {
        ProduceConsumerTest pct=new ProduceConsumerTest();
        System.out.println("生产者消费者问题");
        SynStack stack=pct.new SynStack();
        Produce p=pct.new Produce(stack);
        Consumer c=pct.new Consumer(stack);
        new Thread(p).start();
        new Thread(c).start();
    }

    // NotePad 对象
    class NotePad {
        private int id;
        public NotePad(int id) {
            this.id = id;
        }
        @Override
        public String toString() {
            return "NotePad id" + this.id;
        }
    }

    // 栈对象
    class SynStack {
        int top = -1;
        NotePad[] arr = new NotePad[5];

        public synchronized void push(NotePad notepad) {
            while (top >= arr.length - 1) {
                try {
                    this.wait();//如果栈满了等待消费
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("生产了     " + notepad.toString());
            top++;
            arr[top] = notepad;
            this.notify();//生产以后通知消费者
        }

        public synchronized NotePad pop() {
            while (top == -1) {
                try {
                    this.wait();//如果栈为空等待生产
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            NotePad notepad = arr[top];
            System.out.println("消费者消费了" + notepad);
            top--;
            this.notify();//消费以后发通知生产者
            return notepad;
        }
    }

    // 生产者线程
    class Produce implements Runnable {
        SynStack stack = null;

        public Produce(SynStack stack) {
            this.stack = stack;
        }

        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                NotePad notepad = new NotePad(i);
                stack.push(notepad);
                try {
                    Thread.sleep((int) (Math.random() * 200));//模拟生产时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    // 消费者线程
    class Consumer implements Runnable {
        SynStack stack = null;

        public Consumer(SynStack stack) {
            this.stack = stack;
        }

        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                NotePad notepad = stack.pop();
                try {
                    Thread.sleep((int) (Math.random() * 1000));//模拟消费时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

输出结果:
这里写图片描述

补充:
要理解生产消费者问题,首先应弄清PV操作的含义:
PV操作是由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:
P(S):
①将信号量S的值减1,即S=S-1;
②如果S³0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):
①将信号量S的值加1,即S=S+1;
②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
首先知道P操作与V操作到底有什么作用。
P操作相当于申请资源,而V操作相当于释放资源。
总结:
P操作—-申请资源
V操作—-释放资源

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值