一、实验目的及要求
(1)实验目的:学习使用Thread子类或者Runnable接口创建多线程,实现线程同步机制。
(2)实验要求:两个或者多个线程同时访问同一个变量时,不允许发生数据读取错误,产生数据不一致的情形,要求对实验中出现的数据问题进行分析,确定调试步骤和测试方法,对实验结果进行总结。
二、实验环境(工具、配置等)
1.硬件要求:计算机一台。
2.软件要求:Windows操作系统,使用Java语言,集成开发环境建议使用如Eclipse。
三、实验内容(实验方案、实验步骤、设计思路等)
1.实验方案:实验中,编写多线程程序,模拟两个或者多个线程同时访问同一个变量时,并且一个线程需要修改这个变量,需要对这样的问题进行处理,把修改数据的方法用关键字synchronized修饰,否则可能发生数据错误,从而实现线程同步目的,完成实验报告。
2.实验步骤:根据教材和老师课堂的讲解。
1)创建两个类:Test主类和Warehouse仓库类
2)Test主类中通过Thread 类创建三个线程对象,将Runnable接口的子类对象warehouse作为实际参数传递给Thread类的构造函数,并调用启动入仓库into、清点仓库check、出仓库out三个线程,设定仓库货物初始值。
3)Warehouse仓库类中实现Runnable接口,设置设定修改货物初始值的方法setcargo(),设置同步方法inOrcheckOrout(),在这个方法中实现三个线程分别对仓库的访问和对cargo变量的访问使用和修改,在run()方法中根据线程名字分别调用该同步方法,传入不同的参数。
3.设计思路:
在这个程序中一共有三个线程,即入仓库、清点仓库和出仓库。入仓库、清点仓库和出仓库共同拥有一个仓库,他们都可以使用inOrcheckOrout(int number)方法通过if else if语句对应的对仓库进行访问,if else if语句中通过str.equals()方法来识别是哪个线程。入仓库使用inOrcheckOrout(int number)方法向仓库存入货物;清点仓库使用inOrcheckOrout(int number)方法对仓库的货物进行清点,这个时候需要在清点线程中累加清点货物的数量,然后将这个累计数量赋值给全局变量cargo,为了让出仓库的线程使用;出仓库使用inOrcheckOrout(int number)方法从仓库取出已经清点过的货物。在三个线程访问仓库的时候,设置了分段的工作和休息时间(sleep()方法的使用),但是在休息时间的时候仓库也是归这个线程进行使用的。这三个线程任意一个正在使用inOrcheckOrout(int number)方法的时候,其余两个线程是被禁止使用的,所以inOrcheckOrout(int number)方法是一个synchronized方法。最后在Test主类中调用启动这三个线程。
- 实验结果与分析
- 运行结果如图4-1所示:
图4-1 屏幕输出结果
实验分析:
1.实验遇到的问题:
1)在计算未清点的货物或者计算未出库的货物的时候,如果直接用cargo(总量)减去checkcargo(已清点)或者cargo减去outcargo(已出库)时,屏幕输出的时候会输出负数。
2.问题解决的方法:
1)因为在inOrcheckOrout(int number)这个方法里,每一个线程对仓库的访问是根据仓库货物总量cargo的大小,对传入的参数number进行分割,作为清点或者出库的货物的数量,那么当这个number比较大,会造成checkcargo和outcargo大于cargo的情况,那这个时候肯定是最多清点或者出库了cargo数量的情况,所以用Math.min()方法来确保清点和出库的数量,确保不会有负数情况的出现。
- 附源程序
-
Test类: package shiyanbaogao_eight; public class Test{ public static void main(String args[]) { Warehouse warehouse=new Warehouse(); warehouse.setcargo(200);// 仓库货物初始值 200 Thread into,//入仓库 check,//清点仓库 out;//出货物 into =new Thread(warehouse); check=new Thread(warehouse); out=new Thread(warehouse); into.setName("入仓库"); check.setName("清点仓库"); out.setName("出仓库"); into.start();//启动线程 try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } out.start();//启动线程 try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } check.start();//启动线程 } } Warehouse类: package shiyanbaogao_eight; import java.time.chrono.MinguoChronology; public class Warehouse implements Runnable{ int cargo=100;//设置货物初始值是100 //设置函数 可以修改货物的值 public void setcargo(int m) { // TODO Auto-generated method stub cargo=m; } public void run() { if(Thread.currentThread().getName().equals("入仓库"))//入仓库120货物 { inOrcheckOrout(120); } if(Thread.currentThread().getName().equals("清点仓库"))//清点110货物 { inOrcheckOrout(110); } if(Thread.currentThread().getName().equals("出仓库"))//出仓库100货物 { inOrcheckOrout(100); } } //设置同步方法 入或清点或出 inOrcheckOrout public synchronized void inOrcheckOrout(int number) { if(Thread.currentThread().getName().equals("入仓库")) { for(int i=0;i<4;i++)//分4次入仓库 { cargo=cargo+number/4;// 每次30件入仓库 System.out.println(Thread.currentThread().getName()+ "入了"+number/4+"货物到仓库,目前仓库货物总量:"+cargo+"件,休息一下再入"); try { Thread.sleep(1000);// 当前线程休眠1s } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } else if(Thread.currentThread().getName().equals("清点仓库")) { int checkcargo=0;//初始清点货物量为0 int checkcargosum=0;//设置清点货物总量 for(int i=0;i<4;i++)//设置四次循环 { //下面根据仓库货物总量进行清点 if(cargo>=250) { checkcargo=number/2; } else if(cargo >= 100 && cargo < 250) { checkcargo=number/4; } else if(cargo>= 50 && cargo < 100) { checkcargo=number/5; } else if(cargo>=0 && cargo<50){ checkcargo=number/10; } // 这里的货物代表的是 还没有进行清点的 // 这里减掉的是这俩之间的最小值 是因为要么清点的比总量少 要么清点的和总量一样多 cargo=cargo-Math.min(cargo, checkcargo);//调用java.lang.Math中的min()函数 System.out.println(Thread.currentThread().getName()+ "清点了"+Math.min(checkcargo, cargo)+"件货物,还有"+cargo+"件未清点,休息一下再清点"); checkcargosum+=Math.min(cargo,checkcargo);//累加已经清点了的量 try { Thread.sleep(1000);// 当前线程休眠1s } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } cargo=checkcargosum;//让此时 仓库总量变为已经清点了的量 用来出库 } else {//出仓库 for(int i=0;i<4;i++)//设置四次循环 { int outcargo=0;//初始清点货物量为0 //下面根据仓库已清点货物总量进行出库 if(cargo>=250) { outcargo=number/2; } else if(cargo >= 100 && cargo < 250) { outcargo=number/4; } else if(cargo>= 50 && cargo < 100) { outcargo=number/5; } else if(cargo>=0 && cargo<50){ outcargo=number/10; } // 这里的货物代表的是 还没有进行清点的 // 这里减掉的是这俩之间的最小值 是因为要么出库的比清点的少 要么清点的和出库的一样多 cargo=cargo-Math.min(cargo, outcargo);//调用java.lang.Math中的min()函数 System.out.println(Thread.currentThread().getName()+ "出库了"+Math.min(outcargo, cargo)+"件货物,还有"+cargo+"件未出库,休息一下再出库"); try { Thread.sleep(1000);// 当前线程休眠1s } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } } }