java线程的死锁和交互

1JAVA 演示多线程死锁:

演示死锁

1. 线程1 首先占有对象1,接着试图占有对象2
2. 线程2 首先占有对象2,接着试图占有对象1
3. 线程1 等待线程2释放对象2
4. 与此同时,线程2等待线程1释放对象1

线程代码如下:

public class Hero {
    public String name;
    public float hp;

    public int damage;

    public void attackHero(Hero h) {
        //把暂停时间去掉,多条线程各自会尽力去占有CPU资源
        //线程的优先级效果才可以看得出来
//        try {
//
//            Thread.sleep(0);
//        } catch (InterruptedException e) {
//            // TODO Auto-generated catch block
//            e.printStackTrace();
//        }
        h.hp-=damage;
        System.out.format("%s 正在攻击 %s, %s的血变成了 %.0f%n",name,h.name,h.name,h.hp);

        if(h.isDead())
            System.out.println(h.name +"死了!");
    }

    public boolean isDead() {
        return 0>=hp?true:false;
    }
}
  public static void main(String[] args) {
        final Hero ahri = new Hero();
        ahri.name = "九尾妖狐";
        final Hero annie = new Hero();
        annie.name = "安妮";

        Thread t1 = new Thread(){
            public void run(){
                //占有九尾妖狐
                synchronized (ahri) {
                    System.out.println("t1 已占有九尾妖狐");
                    try {
                        //停顿1000毫秒,另一个线程有足够的时间占有安妮
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    System.out.println("t1 试图占有安妮");
                    System.out.println("t1 等待中 。。。。");
                    synchronized (annie) {
                        System.out.println("do something");
                    }
                }

            }
        };
        t1.start();
        Thread t2 = new Thread(){
            public void run(){
                //占有安妮
                synchronized (annie) {
                    System.out.println("t2 已占有安妮");
                    try {

                        //停顿1000毫秒,另一个线程有足够的时间占有暂用九尾妖狐
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println("t2 试图占有九尾妖狐");
                    System.out.println("t2 等待中 。。。。");
                    synchronized (ahri) {
                        System.out.println("do something");
                    }
                }

            }
        };
        t2.start();
    }

练习-死锁 

3个同步对象a, b, c
3个线程 t1,t2,t3
故意设计场景,使这3个线程彼此死锁

 public static void main(String[] args) {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();
 
        Thread t1 =new Thread(){
            public void run(){
                synchronized (a) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    synchronized (b) {
                        synchronized (c) {
                             
                        }
                    }
                }  
            }
        };
        Thread t2 =new Thread(){
            public void run(){
                synchronized (c) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    synchronized (a) {
                        synchronized (b) {
                             
                        }
                    }
                }  
            }
        };
        Thread t3 =new Thread(){
            public void run(){
                synchronized (b) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    synchronized (c) {
                        synchronized (a) {
                             
                        }
                    }
                }  
            }
        };
 
        t1.start();
        t2.start();
        t3.start();
        
   }

2JAVA 线程之间的交互 WAIT和NOTIFY

线程之间有交互通知的需求,考虑如下情况:
有两个线程,处理同一个英雄。
一个加血,一个减血。
减血的线程,发现血量=1,就停止减血,直到加血的线程为英雄加了血,才可以继续减血

1不好的解决方式:

故意设计减血线程频率更高,盖伦的血量迟早会到达1
减血线程中使用while循环判断是否是1,如果是1就不停的循环,直到加血线程回复了血量
这是不好的解决方式,因为会大量占用CPU,拖慢性能

/**
 * java线程之间的交互
 */
public class Hero {
    public String name;
    public float hp;

    public int damage;

    public synchronized  void recover(){
        hp=hp+1;
    }
    public synchronized void hurt(){
        hp=hp-1;
    }

    public void attackHero(Hero h) {
        h.hp-=damage;
        System.out.format("%s 正在攻击 %s, %s的血变成了 %.0f%n",name,h.name,h.name,h.hp);
        if(h.isDead())
            System.out.println(h.name +"死了!");
    }

    public boolean isDead() {
        return 0>=hp?true:false;
    }
}
 public static void main(String[] args) {

        final Hero gareen = new Hero();
        gareen.name = "盖伦";
        gareen.hp = 616;

        Thread t1 = new Thread(){
            public void run(){
                while(true){

                    //因为减血更快,所以盖伦的血量迟早会到达1
                    //使用while循环判断是否是1,如果是1就不停的循环
                    //直到加血线程回复了血量
                    while(gareen.hp==1){
                        continue;
                    }

                    gareen.hurt();
                    System.out.printf("t1 为%s 减血1点,减少血后,%s的血量是%.0f%n",gareen.name,gareen.name,gareen.hp);
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

            }
        };
        t1.start();

        Thread t2 = new Thread(){
            public void run(){
                while(true){
                    gareen.recover();
                    System.out.printf("t2 为%s 回血1点,增加血后,%s的血量是%.0f%n",gareen.name,gareen.name,gareen.hp);

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

            }
        };
        t2.start();

    }

2使用wait和notify进行线程交互:

在Hero类中:hurt()减血方法:当hp=1的时候,执行this.wait().
this.wait()表示 让占有this的线程等待,并临时释放占有
进入hurt方法的线程必然是减血线程,this.wait()会让减血线程临时释放对this的占有。 这样加血线程,就有机会进入recover()加血方法了。
recover() 加血方法:增加了血量,执行this.notify();
this.notify() 表示通知那些等待在this的线程,可以苏醒过来了。 等待在this的线程,恰恰就是减血线程。 一旦recover()结束, 加血线程释放了this,减血线程,就可以重新占有this,并执行后面的减血工作。

代码如下:

public class Hero {
    public String name;
    public float hp;
 
    public int damage;
 
    public synchronized void recover() {
        hp = hp + 1;
        System.out.printf("%s 回血1点,增加血后,%s的血量是%.0f%n", name, name, hp);
        // 通知那些等待在this对象上的线程,可以醒过来了,如第20行,等待着的减血线程,苏醒过来
        this.notify();
    }
 
    public synchronized void hurt() {
        if (hp == 1) {
            try {
                // 让占有this的减血线程,暂时释放对this的占有,并等待
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        hp = hp - 1;
        System.out.printf("%s 减血1点,减少血后,%s的血量是%.0f%n", name, name, hp);
    }
 
    public void attackHero(Hero h) {
        h.hp -= damage;
        System.out.format("%s 正在攻击 %s, %s的血变成了 %.0f%n", name, h.name, h.name, h.hp);
        if (h.isDead())
            System.out.println(h.name + "死了!");
    }
 
    public boolean isDead() {
        return 0 >= hp ? true : false;
    }
 
}
 public static void main(String[] args) {
    
        final Hero gareen = new Hero();
        gareen.name = "盖伦";
        gareen.hp = 616;
             
        Thread t1 = new Thread(){
            public void run(){
                while(true){
                       
                    //无需循环判断
//                    while(gareen.hp==1){
//                        continue;
//                    }
                       
                    gareen.hurt();
                     
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
   
            }
        };
        t1.start();
   
        Thread t2 = new Thread(){
            public void run(){
                while(true){
                    gareen.recover();
   
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
   
            }
        };
        t2.start();
             
    }

留意wait()和notify() 这两个方法是什么对象上的?

这里需要强调的是,wait方法和notify方法,并不是Thread线程上的方法,它们是Object上的方法。
因为所有的Object都可以被用来作为同步对象,所以准确的讲,wait和notify是同步对象上的方法。
wait()的意思是: 让占用了这个同步对象的线程,临时释放当前的占用,并且等待。 所以调用wait是有前提条件的,一定是在synchronized块里,否则就会出错。
notify() 的意思是,通知一个等待在这个同步对象上的线程,你可以苏醒过来了,有机会重新占用当前对象了。
notifyAll() 的意思是,通知所有的等待在这个同步对象上的线程,你们可以苏醒过来了,有机会重新占用当前对象了。

练习-线程交互 :

假设加血线程运行得更加频繁,英雄的最大血量是1000
设计加血线程和减血线程的交互,让回血回满之后,加血线程等待,直到有减血线程减血

public class Hero {
    public String name;
    public float hp;
  
    public int damage;
  
    public synchronized void recover() {
        //当血量大于或者等于1000的时候
        //this.wait() 让占用这个对象的线程等待,并临时释放锁
        if(hp>=1000){
            try {
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
         
        hp = hp + 1;
        System.out.printf("%s 回血1点,增加血后,%s的血量是%.0f%n", name, name, hp);
        this.notify();
    }
  
    public synchronized void hurt() {
        if (hp == 1) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
  
        hp = hp - 1;
        System.out.printf("%s 减血1点,减少血后,%s的血量是%.0f%n", name, name, hp);
        //掉血之后,唤醒等待的线程
        this.notify();
    }
  
    public void attackHero(Hero h) {
        h.hp -= damage;
        System.out.format("%s 正在攻击 %s, %s的血变成了 %.0f%n", name, h.name, h.name, h.hp);
        if (h.isDead())
            System.out.println(h.name + "死了!");
    }
  
    public boolean isDead() {
        return 0 >= hp ? true : false;
    }
  
public static void main(String[] args) {
     
        final Hero gareen = new Hero();
        gareen.name = "盖伦";
        gareen.hp = 616;
              
        Thread t1 = new Thread(){
            public void run(){
                while(true){
                    gareen.hurt();
                      
                    try {
                        //减慢掉血的速度
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
    
            }
        };
        t1.start();
    
        Thread t2 = new Thread(){
            public void run(){
                while(true){
                    gareen.recover();
    
                    try {
                        //加快回血的速度
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
    
            }
        };
        t2.start();
              
    }

在上面的练习的基础上,增加回血线程到2条,减血线程到5条,同时运行。
运行一段时间,观察会发生的错误,分析错误原因,并考虑解决办法


//把if改为while,被唤醒后,会重复查看hp的值,只有hp大于1,才会往下执行

//把if改为while,被唤醒后,会重复查看hp的值,只有hp大于1,才会往下执行

//if(hp <= 1) {

while (hp <= 1) {

try {

this.wait();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值