Java第十次作业


一、学习总结

1.用思维导图对java多线程的学习内容进行总结。

2.下面是一个单线程实现的龟兔赛跑游戏。

public class TortoiseHareRace {
    public static void main(String[] args) {
        int totalStep = 10;
        int tortoiseStep = 0;
        int hareStep = 0;
        boolean[] flags = {true,false};
        System.out.println("龟兔赛跑开始了...");
        while(tortoiseStep < totalStep && hareStep < totalStep){
            tortoiseStep++;
            System.out.println("乌龟跑了"+tortoiseStep+"步...");
            boolean isHareSleep = flags[((int)(Math.random()*10))%2];
            if(isHareSleep){
                System.out.println("兔子睡着了zzzz");
            }else{
                hareStep += 2;
                System.out.println("兔子跑了"+hareStep+"步...");
            }
        }       
    }
}

阅读程序,采用实现Runnable接口的方式用多线程实现这个小游戏。下面给出主线程类,补充Tortoise线程类和Hare线程类。

public class TortoiseHareRace { 
    public static void main(String[] args) {
        Tortoise tortoise = new Tortoise(10);
        Hare hare = new Hare(10);
        Thread tortoiseThread = new Thread(tortoise);
        Thread hareThread = new Thread(hare);
        tortoiseThread.start();
        hareThread.start();
    }
}
  • 补充后:
public class TortoiseHareRace {
    public static void main(String[] args) {
        Tortoise tortoise = new Tortoise(10);
        Hare hare = new Hare(10);
        Thread tortoiseThread = new Thread(tortoise);
        Thread hareThread = new Thread(hare);
        tortoiseThread.start();
        hareThread.start();
    }
}
class Hare implements Runnable{
	private int number;
	private int hareStep=0;
    boolean[] flags = {true,false};
	public Hare(int number){
		this.number=number;
	}
	public void run() {
		while(number>0){
            try {
                Thread.sleep(300);
            boolean isHareSleep = flags[((int)(Math.random()*10))%2];
            if(isHareSleep){
                System.out.println("兔子睡着了zzzz");
            }else{
                hareStep += 2;
                this.number-=2;
                System.out.println("兔子跑了"+hareStep+"步...");
            }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }  
		}
		synchronized (this){
			System.out.println("兔子到达终点,兔子获胜!");
			System.exit(0);
		}
	}
}
class Tortoise implements Runnable{
	private int number;
	private int tortoiseStep = 0;
	public Tortoise(int number){
		this.number=number;
	}
	public void run() {
		while(number>0){
            try {
                Thread.sleep(300);
			tortoiseStep++;
			this.number--;
			System.out.println("乌龟跑了"+tortoiseStep+"步...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
		}
		synchronized (this){
			System.out.println("乌龟到达终点,乌龟获胜!");
			System.exit(0);
		}
	}
}

3.下面的程序是模拟了生产者——消费者问题,生产者生产10个数,消费者依次消费10个数,运行程序,看结果是否正常?存在什么问题?说明原因。使用synchronized, wait, notify解决程序出现的问题。写出修改的部分程序即可。

class Consumer implements Runnable {
    private Clerk clerk;
    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }
    public void run() {
        System.out.println("消费者开始消耗整数......");
        // 消耗10个整数
        for(int i = 1; i <= 10; i++) {
            try {
                 // 等待随机时间
                Thread.sleep((int) (Math.random() * 3000));
            }
            catch(InterruptedException e) {
                e.printStackTrace();
            }              
            clerk.getProduct();// 从店员处取走整数
        }
    }
 }
class Producer implements Runnable {
    private Clerk clerk;
    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }
    public void run() {
        System.out.println( "生产者开始生产整数......");
        // 生产1到10的整数
        for(int product = 1; product <= 10; product++) {
            try {
                Thread.sleep((int) Math.random() * 3000);
            }
            catch(InterruptedException e) {
                e.printStackTrace();
            }
           clerk.setProduct(product); // 将产品交给店员
        }
    } 
}
public class ProductTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Thread consumerThread = new Thread(new Consumer(clerk)); 
        Thread producerThread = new Thread(new Producer(clerk)); 
        consumerThread.start(); 
        producerThread.start(); 
    }
}
class Clerk {
    private int product = -1; // -1 表示目前没有产品 
     // 这个方法由生产者呼叫
    public void setProduct(int product) {
        this.product = product; 
        System.out.printf("生产者设定 (%d)%n", this.product);      
    } 
    // 这个方法由消费者呼叫
    public int getProduct() {          
        int p = this.product; 
        System.out.printf("消费者取走 (%d)%n", this.product);      
        return p; 
    } 
}
  • 运行结果如下图:
  • 存在问题:消费者一直在取走(10)。
  • 原因:消费者比生产者快时,消费者取相同的数据。需要进行对共享对象加锁,并且两个线程之间互相通知。
  • 修改后程序:(需要让生产者生产一个提醒消费者,等消费者取走后提醒生产者生产,加入等待和通知)
class Consumer implements Runnable {
    private Clerk clerk;
    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }
    public void run() {
        System.out.println("消费者开始消耗整数......");
        // 消耗10个整数
        for(int i = 1; i <= 10; i++) {
            try {
                 // 等待随机时间
                Thread.sleep((int) (Math.random() * 3000));
            }
            catch(InterruptedException e) {
                e.printStackTrace();
            }              
            clerk.getProduct();// 从店员处取走整数
        }
    }
 }
class Producer implements Runnable {
    private Clerk clerk;
    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }
    public void run() {
        System.out.println( "生产者开始生产整数......");
        // 生产1到10的整数
        for(int product = 1; product <= 10; product++) {
            try {
                Thread.sleep((int) Math.random() * 3000);
            }
            catch(InterruptedException e) {
                e.printStackTrace();
            }
           clerk.setProduct(product); // 将产品交给店员
        }
    } 
}
public class ProductTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Thread consumerThread = new Thread(new Consumer(clerk)); 
        Thread producerThread = new Thread(new Producer(clerk)); 
        consumerThread.start(); 
        producerThread.start(); 
    }
}
class Clerk {
	private boolean flag=true; 
    private int product = -1; // -1 表示目前没有产品 
     // 这个方法由生产者呼叫
    public synchronized void setProduct(int product) {
        while(this.flag == false){
            try {
                    wait();
            } catch (InterruptedException e) {  
            	e.printStackTrace();  
            }
         }
        this.product = product; 
        System.out.printf("生产者设定 (%d)%n", this.product);
        this.flag = false;
        super.notify();
    } 
    // 这个方法由消费者呼叫
    public synchronized int getProduct() {   
        while(this.flag == true){
            try {
                    wait();
            } catch (InterruptedException e) {  
            	e.printStackTrace();  
            }
         }
        int p = this.product; 
        System.out.printf("消费者取走 (%d)%n", this.product);  
        this.flag = true;
        super.notify();
        return p; 
    } 
}
  • 修改后的运行结果:

二、实验总结:

1.模拟三个老师同时分发80分作业,每个老师相当于一个线程。

  • 程序设计思路:
    - 新建一个教师类实现Runnable接口,定义一个私有属性存放试卷份数,实现接口的run()方法;run()方法中有相应的输出。
    - 新建一个测试类,主方法中定义一个线程对象,建三个Thread匿名对象分别启动三个线程。
  • 问题1:没有实现试卷份数共享,重复发放试卷。
  • 原因:因为在定义了三个Teacher类对象,分别启动一个线程,每个老师发放试卷是独立的,没有共享试卷。
  • 解决方案:在主方法中只定义一个Teacher类对象,用这个对象做参数建三个Thread匿名对象并分别启动线程。部分代码如下:
public static void main(String args[]){
	Teacher teathers=new Teacher();//定义线程对象
    new Thread(teathers,"李").start();//启动一个线程
    new Thread(teathers,"刘").start();//启动一个线程
    new Thread(teathers,"张").start();//启动一个线程
}

2.模拟一个银行存款的程序。假设有两个储户都去银行往同一个账户进行存款,一次存100,每人存三次。要求储户每存一次钱,账户余额增加100,并在控制台输出当前账户的余额。

  • 程序设计思路:
    - 新建一个银行类,有余额私有数据成员,存钱方法和获取余额的方法。
    - 新建一个储户类实现Runnable接口,定义一个私有用户对象user,实现接口的run()方法;run()方法里面三次用user对象调用存钱方法并相应的输出(每一次的调用和输出运用了同步代码块)。
    - 新建一个测试类在主方法中定义一个线程对象,然后建用两个Thread匿名对象分别启动两个线程。
  • 问题1:两个用户一个存钱次数一共为3次,而不是分别3次。
  • 原因:将次数设置成了私有属性被两个线程共享了,任意一个用户每存一次钱,次数减一。
  • 解决方案:将这个私有属性删去,用for循环3次实现。部分代码如下:
public void run() {//实现接口的run方法
	for(int i=0;i<3;i++){//循环3次
		bank.save(100);//存钱
		System.out.println(Thread.currentThread().getName()+"正在第"+(i+1)+"次存入100元,当前余额为:"+bank.getMoney());
	}
}
  • 问题2:出现运行结果如下图:
  • 原因:因为未考虑同步问题。
  • 设置同步代码块。部分代码如下:
for(int i=0;i<3;i++){//循环3次
	synchronized (this){//同步代码块
		bank.save(100);//存钱
		System.out.println(Thread.currentThread().getName()+"正在第"+(i+1)+"次存入100元,当前余额为:"+bank.getMoney());
	}
}
  • 问题3:运行结果很快就打出来并不是一个一个的存钱。
  • 原因:每次存钱时没有加入缓冲。
  • 解决方案:加入Thread.sleep(300);语句,并进行异常捕获。部分代码如下:
	public void run() {// 实现接口的run方法
		for (int i = 0; i < 3; i++) {// 3次循环存钱
			synchronized (this) {
				try {
					Thread.sleep(300);// 缓冲
					bank.save(100);// 存钱
					System.out.println(Thread.currentThread().getName() + "正在第"+ (i + 1) + "次存入100元,当前余额为:" + bank.getMoney());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

三、代码托管(务必链接到你的项目)

  • 码云commit历史截图
    上传实验项目代码到码云,在码云项目中选择“统计-commits”,设置搜索时间段,搜索本周提交历史,并截图。

posted @ 2017-06-01 17:33  暖心物语  阅读(250)  评论(0编辑  收藏  举报