学习例子是参照《thinking in java》中修改的,先贴上运行结果:

注意看红框之中的内容,这个仿真要达到这样一个目的:

1.客户队列(无优先级):每隔300MILLS生产一个客户

2.正在服务的出纳员队列(有优先级):队列头始终是服务人数最多的那个出纳员,因为在队列调整的时候需要将服务人数最多的出纳员调整出去做其他的事情(相当于找个轻松的事情做,放松下)

3.做其他事情的出纳员列表(有优先级):队列头始终是服务人数最少的那个出纳员,因为在队列调整的时候需要讲服务人数最少的出纳员调整去服务(为客户办理业务)

队列调整时间为每1秒调整一次

下面贴上代码:

 package com.xt.thinks21_8;

 import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; /**
* 消费者(客户)
*
* @author Administrator
*
*/
class Customer {
private final int serviceTime;// 服务时间(客户的业务办理时间) public Customer(int tm) {
serviceTime = tm;
} public int getServiceTime() {
return serviceTime;
} public String toString() {
return " ● ";
}
} /**
* 消费者队列(客户排成一条队伍)
*
* @author Administrator
*
*/
class CustomerLine extends ArrayBlockingQueue<Customer> {
/**
*
*/
private static final long serialVersionUID = 1L; /**
* 构造方法传入客户最大人数
*
* @param maxLineSize
*/
public CustomerLine(int maxLineSize) {
super(maxLineSize);
} /**
* 返回所有客户(服务时间)
*/
public String toString() {
if (this.size() == 0)
return "[Empty]"; StringBuilder result = new StringBuilder();
for (Customer customer : this) {
result.append(customer);
}
return result.toString();
}
} /**
* 消费者产生器
*
* @author Administrator
*
*/
class CustomerGenerator implements Runnable {
private CustomerLine customers;
private static Random rand = new Random(47); public CustomerGenerator(CustomerLine cq) {
customers = cq;
} public void run() {
try {
while (!Thread.interrupted()) {
// 每隔300MILLS就假如一个客户(来办理业务等)
TimeUnit.MILLISECONDS.sleep(rand.nextInt(300));
// 将客户加入到消费者队列
customers.put(new Customer(rand.nextInt(1000)));
// System.out.println(customers.toString());
}
} catch (InterruptedException e) {
System.out.println("CustomerGenerator interrupted");
}
System.out.println("CustomerGenerator terminating");
}
} /**
* 出纳员,业务员
*
* @author Administrator
*
*/
class Teller implements Runnable, Comparable<Teller> {
private static int counter = 0;
private final int id = counter++;// 业务员的id
private int customersServed = 0;// 已经服务的客户
private CustomerLine customers;// 客户队列
private boolean servingCustomerLine = true;// 是否正在服务客户
private int count = 0;
private boolean sort = true;// 排序方式,true为降序,false升序 public Teller(CustomerLine cq) {
customers = cq;
} @Override
public void run() {
try {
while (!Thread.interrupted()) {
// 下面两行代表正在为客户服务
Customer customer = customers.take();
count++;
TimeUnit.MILLISECONDS.sleep(customer.getServiceTime());
synchronized (this) {// 这里同步的原因是因为禁止执行到这里时线程调用doSomethingElse(),serveCustomerLine(),以及compareTo(Tellero)
customersServed++;// 服务的客户+1
while (!servingCustomerLine)
// 当出纳员去干其他的事情的时候servingCustomerLine会为false
wait();// 代表出纳员正在做其他事情
}
}
} catch (InterruptedException e) {
System.out.println(this + " interrupted");
}
System.out.println(this + " terminating");// 结束
} /**
* 出纳员做其他的事情
*/
public synchronized void doSomethingElse() {
// customersServed = 0;//服务过的客户置0
servingCustomerLine = false;
} /**
* 出纳员服务客户队列
*/
public synchronized void serveCustomerLine() {
assert !servingCustomerLine : "already serving:" + this;
servingCustomerLine = true;// 标识修改
notifyAll();// 唤醒线程
} public String toString() {
return "Teller " + id + ":" + count;
} public String shortString() {
return "T" + id;
} public int getCount() {
return count;
} public void setCount(int count) {
this.count = count;
} public boolean isSort() {
return sort;
} public void setSort(boolean sort) {
this.sort = sort;
} /**
* 优先级队列的优先级比较方法
*/
@Override
public synchronized int compareTo(Teller o) {
// TODO Auto-generated method stub
if (sort)// 降序(目的为了加入到workingTellers队列中)
return o.customersServed - customersServed > 0 ? 1
: o.customersServed - customersServed < 0 ? -1 : 0;
else
// 升序(目的为了加入到tellersDoingOtherThings队列中)
return customersServed - o.customersServed > 0 ? 1
: customersServed - o.customersServed < 0 ? -1 : 0;
} } /**
* 出纳员管理器
*
* @author Administrator
*
*/
class TellerManager implements Runnable {
private ExecutorService exec;// 执行器
private CustomerLine customers;// 客户队列
private PriorityQueue<Teller> workingTellers = new PriorityQueue<Teller>();// 降序队列
private PriorityQueue<Teller> tellersDoingOtherThings = new PriorityQueue<Teller>();// 升序队列
private int adjustmentPeriod;// 调整周期 public TellerManager(ExecutorService e, CustomerLine customers,
int adjustmentPeriod) {
exec = e;
this.customers = customers;
this.adjustmentPeriod = adjustmentPeriod;
// 构造出纳员管理器的时候默认新增一个出纳员
Teller teller = new Teller(customers);// 默认为降序
exec.execute(teller);
workingTellers.add(teller);
} /**
* 调整出纳员数量
* */
public void adjustTellerNumber() {
try {
// 利用反射调用PriorityQueue的heapify(刷新队列头)方法
Method method = workingTellers.getClass().getDeclaredMethod(
"heapify");
method.setAccessible(true);
try {
method.invoke(workingTellers);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // 如果客户队列的size大于出纳员队列的size的两倍,新增出纳员
if (customers.size() / workingTellers.size() > 2) {
if (tellersDoingOtherThings.size() > 0) {
Teller teller = tellersDoingOtherThings.remove();
teller.setSort(true);// 设置为降序
teller.serveCustomerLine();
workingTellers.add(teller);
return;
}
Teller teller = new Teller(customers);// 默认为降序
exec.execute(teller);
workingTellers.add(teller);
return;
}
// 如果出纳员的size大于1并且客户队列的size小雨出纳员size的两倍,调用一个出纳员去做其他的事情,从出纳员队列中移除
if (workingTellers.size() > 1
&& customers.size() / workingTellers.size() < 2)
reassignOneTeller();
// 如果客户队列size==0并且出纳员的size大于1,同样移除一个出纳员去做其他的事情
if (customers.size() == 0)
while (workingTellers.size() > 1)
reassignOneTeller();
} /**
* 移除一个出纳员去做其他事情
* */
private void reassignOneTeller() {
Teller teller = workingTellers.poll();
teller.setSort(false);
teller.doSomethingElse();
tellersDoingOtherThings.add(teller);
} public void run() {
try {
while (!Thread.interrupted()) {
adjustTellerNumber();
TimeUnit.MILLISECONDS.sleep(adjustmentPeriod);
// 打印客户和出纳员队列
System.out.print("customers[" + customers
+ "]    workingTellers[");
for (Teller teller : workingTellers) {
System.out.print(teller.shortString() + ":"
+ teller.getCount() + " ");
}
// 打印在做其他事情的出纳员队列
System.out.print("]    doingOtherThingTellers[");
for (Teller teller : tellersDoingOtherThings) {
System.out.print(teller.shortString() + ":"
+ teller.getCount() + " ");
}
if (tellersDoingOtherThings.size() == 0)
System.out.print("Empty");
System.out.println("]\n");
}
} catch (InterruptedException e) {
System.out.println(this + " interrupted");
}
System.out.println(this + " terminating");
} public String toString() {
return "TellerManager";
}
} public class BankTellerSimulation {
static final int MAX_LINE_SIZE = 50;
static final int ADJUSTMENT_PERIOD = 1000; public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
CustomerLine customers = new CustomerLine(MAX_LINE_SIZE);
exec.execute(new CustomerGenerator(customers));
exec.execute(new TellerManager(exec, customers, ADJUSTMENT_PERIOD));
if (args.length > 0)
TimeUnit.SECONDS.sleep(new Integer(args[0]));
else {
System.out.println("Press 'Enter' to quit");
System.in.read();
}
exec.shutdownNow();
}
}

代码中注释唯一需要注意的地方是:

 try {
// 利用反射调用PriorityQueue的heapify(刷新队列头)方法
Method method = workingTellers.getClass().getDeclaredMethod(
"heapify");
method.setAccessible(true);
try {
method.invoke(workingTellers);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

这个地方可以看出PriorityQueue的设计还是可以完善的,如果PriorityQueue中的队列元素是线程,线程内部需要调整比较因子,那么PriorityQueue没有提供出一个可以刷新队列头的方法的,如果在此demo中不刷新队列头而部分线程(出纳员)服务人数增长过快超过当前队列头的服务人数,则正在服务的队列的排序会造成假象。而heapify这个方法正是刷新队列头的方法可以却没有公开出来,无奈只有通过反射来调用刷新队列头,此处也可以看出reflect机制在JAVA中的强大。

如有错误还请提出指正

JAVA仿真之银行出纳员的更多相关文章

  1. Thinking in Java---多线程仿真:银行出纳员仿真+饭店仿真+汽车装配工厂仿真

    多线程一个非常有意思的作用就是用于仿真,这篇博客就会结合几个仿真实例来综合运用一下前面所学的多线程并发知识. 一.银行出纳员仿真 问题描写叙述:银行会有非常多来办业务的顾客,他们会排队等待服务:对于银 ...

  2. Java多线程之银行出纳员仿真

    package concurrent; import java.util.LinkedList; import java.util.PriorityQueue; import java.util.Qu ...

  3. 编写Java程序_银行终端服务系统

    目录 一.General description 总体概述 二.About the Project 项目介绍 三.Soft function 软件功能 四.UI Model Use Case Diag ...

  4. 基于Java的简单银行管理系统(MVC设计模式)

    项目导航 功能展示 项目描述 项目结构 `data` `service` `utils` `view ` 欠缺与总结 源码下载 功能展示 本系统基于命令台窗口,暂未与图形页面结合.话不多说,先上效果图 ...

  5. java实现银行管理系统

    Bank类 package First; import java.util.TreeSet; //银行类public class Bank {        private String Bankna ...

  6. JAVA上百实例源码以及开源项目

    简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级.中级.高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情.执着,对IT的憧憬. ...

  7. JAVA上百实例源码网站

    JAVA源码包1JAVA源码包2JAVA源码包3JAVA源码包4 JAVA开源包1 JAVA开源包2 JAVA开源包3 JAVA开源包4 JAVA开源包5 JAVA开源包6 JAVA开源包7 JAVA ...

  8. JSP/JAVA目录清单

    JAVA253中国象棋(CS) JAVA258网络五子棋游戏的设计与实现(CS) JAVA390停车场管理系统SQL(CS) JSP001学生综合素质测评系统JAVA+Mysql JSP002学生成绩 ...

  9. 作为Java开发人员不会饿死的5个理由

    尽管已有20多年的历史,Java仍然是最广泛使用的编程语言之一.只需看看统计数据:根据2018年Stack Overflow开发人员调查,Java是世界上第三大最受欢迎的技术. TIOBE指数,这是一 ...

随机推荐

  1. linux 让一个程序开机自启动并把一个程序加为服务

    本文以tomcat7为例 首先找到tomcat启动的目录,我的为 cd /usr/local/tomcat7/bin/ 这个目录 启动脚本是startup.sh 然后在/etc/rc.d/rc.loc ...

  2. Linux定时任务Crontab命令详解

    linux 系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的.另 外, 由于使用者自己也可以设置计划任务,所以, ...

  3. Flink Program Guide (2) -- 综述 (DataStream API编程指导 -- For Java)

    v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...

  4. CmdParse

    Procedure URPOSE Uses Dos,Crt; Const VersionNum = 'V1.0 BETA'; ProgNameStr = 'NEWPROJ.EXE'; ProgName ...

  5. JQuery DataTables Editor---只修改页面内容

    近来在工作中需要对JQuery DataTables进行增,删,改的操作,在网上找了一些资料,感觉比较的好的就是(http://editor.datatables.net/)文章中所展示的操作方法(如 ...

  6. (asp.net MVC学习)System.Web.Mvc.HtmlHelper学习及使用

    在ASP.NET MVC框架中没有了自己的控件,页面显示完全就回到了写html代码的年代.还好在asp.net mvc框架中也有自带的HtmlHelper和UrlHelper两个帮助类.另外在MvcC ...

  7. Oracle EBS-SQL (BOM-8):检查物料属性(无采购员).sql

    select       msi.segment1                                  物料编码,       msi.DESCRIPTION               ...

  8. ASP.net MVC3里的HandleErrorAttribute

    在MVC3里使用HandleErrorAttribte类,有2种方式可以使用它,一是在类或者方法上直接使用HandleError属性来定义: // 在这里声明[HandleError]public c ...

  9. STM32与LPC系列ARM资源之比较

    由于有周立公开发板的影响,LPC系列的开发板在工程师心目中一般是入门的最好型号之一.这次刚好有STM32的竞赛,正好将两者的资源进行比较一下(LPC系列以LPC213X为例). LPC213X包括LP ...

  10. 关于关注和取消关注的nodejs写法

    本例子的关注和取消关注,是通过ajax的方法实现的:nodejs后台写好api接口:响应前台的ajax 先看ajax的代码实现: // 用户关注标签 function subscribe(uid, t ...