线程2

即上次的线程问题,这次来写个例子

张三吃包子问题。我们来看代码

/**
 * 包子类
 */
public class Package {


    //包子个数
    private int p;
    //初始化包子个数为100
    public  Package(){
        p = 100;
    }
    public int getP() {
        return p;
    }

    public void setP(int p) {
        this.p = p;
    }
    //同步方法 锁
    public synchronized  void  t(Person person ){
        System.out.println(person.getName1() + "吃了" + this.getP()+ "个包子");
                this.setP(this.getP() - 1);

    }

}

 

 

public class Person extends Thread {

    public String getName1() {
        return name1;
    }


    public void setName1(String name) {
        this.name1 = name;
    }

    private String name1;

    private  Package p= null;

    public Person(String name,Package p){
        this.name1 = name;
        this.p = p;

    }
    @Override
    public void run(){
        try{
            while(true){

             p.t(this);
                Thread.sleep(1);
                if(p.getP()<=0){
                    System.exit(0);
                }

            //同步块,监视器,两个线程都能访问的同一个
//            synchronized (p) {
//                System.out.println(this.name + "吃了" + p.getP() + "个包子");
//                p.setP(p.getP() - 1);
//                Thread.sleep(1);
//            }
            }
           }catch (Exception e){
              e.printStackTrace();
        }


    }

}
public static void main(String[] args){
    Package P = new Package();
    Person p1 = new Person("张三",P);
    Person p2 = new Person("李四",P);
    p1.start();
    p2.start();

}

运行结果:

其实,代码初步并不是这样,由此例子引出下面几个问题

1.线程争用
2.线程争用问题解决 用 sychronized同步块或同步方法
3.线程同步 sychronized同步块 加锁
4.同步方法 有锁
5.线程死锁
6.线程协作

线程争用:

原始的代码是这样:

 public void run(){
        try{
            while(true){
                if(p.getP()<=0){
                    System.exit(0);
                }
                System.out.println(this.name1 + "吃了" + p.getP() + "个包子");
                 p.setP(p.getP() - 1);
            }
           }catch (Exception e){
              e.printStackTrace();
        }


    }

 

运行结果是这样:

   

为什么会出现这样的情况呢?

 这是因为吃包子和包子数减1,是两个不可分割的操作。

当张三的线程吃包子后,还没数量减1,这个线程时间就用完了,到李四线程,此时包子数量还是100,李四又继续吃包子,包子数量减1后,这样李四线程时间刚好用完,到张三吃包子,张三就从99开始,造成这个问题原因是什么?是因为每个线程时间不确定,两个不能分割的操作不能在同一段时间内完后,就出现了资源争用问题。

如何解决这个问题呢?

线程同步

解决这个问题,必须将不能分割的两个操作同步在一起,叫同步块,用sychronized来实现线程同步

public void run(){
        try{
            while(true){
                if(p.getP()<=0){
                    System.exit(0);
                }


            //同步块,监视器,两个线程都能访问的同一个
            synchronized (p) {
                System.out.println(this.name1 + "吃了" + p.getP() + "个包子");
                p.setP(p.getP() - 1);
                Thread.sleep(1);
            }
            }
           }catch (Exception e){
              e.printStackTrace();
        }


    }

 

将两个不能分割的操作同步到一起。运行结果

这样就解决了不同线程访问同一资源而导致的线程争用冲突

synchronized解析:

 这里我们加锁的对象为包子,因为包子是两个线程共同访问的对象,同一个资源。

同步方法

除了这个的同步块,我们还可以实现同步方法。

代码改造:

因为包子是共同访问的对象,所以这个同步方法写在包子里面,包子就是锁

 //同步方法 锁
 public synchronized  void  t(Person person ){
     System.out.println(person.getName1() + "吃了" + this.getP()+ "个包子");
             this.setP(this.getP() - 1);

}

 调用这样调

 public void run(){
        try{
            while(true){

             p.t(this);
                Thread.sleep(1);
                if(p.getP()<=0){
                    System.exit(0);
                }
          }
            }
           }catch (Exception e){
              e.printStackTrace();
        }


    }

运行结果:

 线程死锁

代码详细:

package cn.edu.xatu.deadlock;

public class T1 {
    public static  String k = "刀";
    public   static  String f = "叉";

}

 

package cn.edu.xatu.deadlock;

public class P implements Runnable {

    @Override
    public void run() {
        try {
            if (Thread.currentThread().getName() == "张三") {
                synchronized (T1.k) {
                    synchronized (T1.f) {

                    }

                }
            } else {
                synchronized (T1.f) {
                    synchronized (T1.k) {

                    }

                }

            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

运行结果:

死锁:所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。上面的例子张三先锁刀再锁叉,李四先锁叉再锁刀,当张三拿到刀等待拿叉的时候,叉已经被李四拿走了,而李四拿了叉再拿刀的时候,到已经被张三拿走了。然后张三等待李四,李四等待张三,互相等待造成死锁,虚拟机也没有停。

死锁深度解析:https://blog.csdn.net/alex123980/article/details/52318016

线程协作:

package cooperation;

public class Producer implements Runnable {
    private String pname;
    private  Package P1;

    public Producer(String name,Package pp){
        this.P1 = pp;
        this.pname = name;
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname;
    }

    public Package getP() {
        return P1;
    }

    public void setP(Package p) {
        P1 = p;
    }


    @Override
    public void run() {
        try {
            int  count = 0;
            while (true) {

                synchronized (P1) {
                if (P1.getP() <= 0) {
                    P1.setP(100);
                    count++;
                    System.out.println(this.pname + "做的100个热腾腾的包子出锅喽!");
                    System.out.println("第"+ count +"锅包子出锅了");

                    }else {
                   
                    P1.wait();//此线程释放锁,在线程休息室等待。wait方法必须持有锁
                     P1.notify();//唤醒其他处于等待室里面的线程
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

 

package cooperation;

public class Consumer implements Runnable {

    private  String Cname;

    public String getCname() {
        return Cname;
    }

    public void setCname(String cname) {
        Cname = cname;
    }

    public Package getP() {
        return P1;
    }

    public void setP(Package p) {
        P1 = p;
    }

    private Package P1;

    public Consumer (String name,Package PP){
        this.P1 =PP;
        this.Cname = name;
    }
    @Override
    public void run() {
        try {
            while (true) {
                synchronized (P1) {
                if (P1.getP()>0) {
                        System.out.println(this.Cname + "吃了第" + P1.getP() + "个包子");
                        P1.setP(P1.getP() - 1);
                }
                else {
                    P1.notify();
                    P1.wait();

                }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

 

运行结果:

 

最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作。

线程协作使用的方法。

获取了锁了的线程A的wait()方法。线程A进入等待队列释放锁,然后,线程B 获取锁。

 

获取了锁的B执行notify方法,释放掉在等待队列当中的A,A想进入下一个wait操作,但B持有者锁。B调用wait释放掉锁,进入等待 队列,此时线程A获取锁,再执行wait方法,以此类推,尽可能减少资源占用。

 同wait 方法一样,若要执行notify 方法,线程也必须持有要调用的实例的锁(这是规则) 。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值