| 
			
	
	
	
						  
						
						
						
	
 
	本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频
	 
			 
	16分9秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项) 步骤 1 : 不好的解决方式 步骤 2 : 使用wait和notify进行线程交互 步骤 3 : 关于wait、notify和notifyAll 步骤 4 : 练习-线程交互 步骤 5 : 答案-线程交互 步骤 6 : 练习-多线程交互 步骤 7 : 答案-多线程交互 步骤 8 : 练习-生产者消费者问题 步骤 9 : 答案-生产者消费者问题 
					故意设计减血线程频率更高,盖伦的血量迟早会到达1 
					
				减血线程中使用while循环判断是否是1,如果是1就不停的循环,直到加血线程回复了血量 这是不好的解决方式,因为会大量占用CPU,拖慢性能 package charactor;
  
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;
    }
  
}
 
								
										
									
								
							
package multiplethread;
   
import java.awt.GradientPaint;
 
import charactor.Hero;
   
public class TestThread {
   
    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();
          
    }
       
}
 
								
										
									
								
							
					在Hero类中:hurt()减血方法:当hp=1的时候,执行this.wait(). 
					
				this.wait()表示 让占有this的线程等待,并临时释放占有 进入hurt方法的线程必然是减血线程,this.wait()会让减血线程临时释放对this的占有。 这样加血线程,就有机会进入recover()加血方法了。 recover() 加血方法:增加了血量,执行this.notify(); this.notify() 表示通知那些等待在this的线程,可以苏醒过来了。 等待在this的线程,恰恰就是减血线程。 一旦recover()结束, 加血线程释放了this,减血线程,就可以重新占有this,并执行后面的减血工作。 package charactor;
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;
	}
}
 
								
										
									
								
							
package multiplethread;
     
import java.awt.GradientPaint;
   
import charactor.Hero;
     
public class TestThread {
     
    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() 这两个方法是什么对象上的? 
					
				public synchronized void hurt() { 。。。 this.wait(); 。。。 } public synchronized void recover() { 。。。 this.notify(); } 这里需要强调的是,wait方法和notify方法,并不是Thread线程上的方法,它们是Object上的方法。 因为所有的Object都可以被用来作为同步对象,所以准确的讲,wait和notify是同步对象上的方法。 wait()的意思是: 让占用了这个同步对象的线程,临时释放当前的占用,并且等待。 所以调用wait是有前提条件的,一定是在synchronized块里,否则就会出错。 notify() 的意思是,通知一个等待在这个同步对象上的线程,你可以苏醒过来了,有机会重新占用当前对象了。 notifyAll() 的意思是,通知所有的等待在这个同步对象上的线程,你们可以苏醒过来了,有机会重新占用当前对象了。 
					假设加血线程运行得更加频繁,英雄的最大血量是1000 
					
				设计加血线程和减血线程的交互,让回血回满之后,加血线程等待,直到有减血线程减血 
					在上面的练习的基础上,增加回血线程到2条,减血线程到5条,同时运行。 
					
				运行一段时间,观察会发生的错误,分析错误原因,并考虑解决办法 
					生产者消费者问题是一个非常典型性的线程交互的问题。 
					
				1. 使用栈来存放数据 1.1 把栈改造为支持线程安全 1.2 把栈的边界操作进行处理,当栈里的数据是0的时候,访问pull的线程就会等待。 当栈里的数据是200的时候,访问push的线程就会等待 2. 提供一个生产者(Producer)线程类,生产随机大写字符压入到堆栈 3. 提供一个消费者(Consumer)线程类,从堆栈中弹出字符并打印到控制台 4. 提供一个测试类,使两个生产者和三个消费者线程同时运行,结果类似如下 : 
				HOW2J公众号,关注后实时获知最新的教程和优惠活动,谢谢。
			 
			 
			
			
			
			
			
		
		提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢	
	 
 |