学习例子是参照《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仿真之银行出纳员的更多相关文章
-
Thinking in Java---多线程仿真:银行出纳员仿真+饭店仿真+汽车装配工厂仿真
多线程一个非常有意思的作用就是用于仿真,这篇博客就会结合几个仿真实例来综合运用一下前面所学的多线程并发知识. 一.银行出纳员仿真 问题描写叙述:银行会有非常多来办业务的顾客,他们会排队等待服务:对于银 ...
-
Java多线程之银行出纳员仿真
package concurrent; import java.util.LinkedList; import java.util.PriorityQueue; import java.util.Qu ...
-
编写Java程序_银行终端服务系统
目录 一.General description 总体概述 二.About the Project 项目介绍 三.Soft function 软件功能 四.UI Model Use Case Diag ...
-
java实现银行管理系统
Bank类 package First; import java.util.TreeSet; //银行类public class Bank { private String Bankna ...
-
JAVA上百实例源码以及开源项目
简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级.中级.高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情.执着,对IT的憧憬. ...
-
JAVA上百实例源码网站
JAVA源码包1JAVA源码包2JAVA源码包3JAVA源码包4 JAVA开源包1 JAVA开源包2 JAVA开源包3 JAVA开源包4 JAVA开源包5 JAVA开源包6 JAVA开源包7 JAVA ...
-
JSP/JAVA目录清单
JAVA253中国象棋(CS) JAVA258网络五子棋游戏的设计与实现(CS) JAVA390停车场管理系统SQL(CS) JSP001学生综合素质测评系统JAVA+Mysql JSP002学生成绩 ...
-
作为Java开发人员不会饿死的5个理由
尽管已有20多年的历史,Java仍然是最广泛使用的编程语言之一.只需看看统计数据:根据2018年Stack Overflow开发人员调查,Java是世界上第三大最受欢迎的技术. TIOBE指数,这是一 ...
-
NIO相关概念之Channel
通道(Channel)是java.nio的第二个主要创新.它们既不是一个扩展也不是一项增强,而是全新.极好的Java I/O示例,提供与I/O服务的直接连接.Channel用于在字节缓冲区和位于通道另 ...
随机推荐
-
js中Unicode转义序列
将某一中文字符转义,可采取在线工具进行转义,http://tool.chinaz.com/tools/unicode.aspx "哈哈" ==="\u54c8\u54c8 ...
-
七层负载均衡——HAProxy
HAProxy入门 HAProxy是一款提供高可用性.负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理软件,HAProxy是完全免费的.借助HAProxy可以快速并且可靠的提供基于TCP ...
-
【Other】千字文 硬笔 楷书 字帖
<千字文>是我国最优秀的一篇训蒙教材,用一千个汉字勾划出一部完整的中国文化史的基本轮廓,代表了中国传统教育启蒙阶段的最高水平.<千字文>通篇首尾连贯,音韵谐美,读起来朗朗上口, ...
-
TCP的三次握手
第一次握手 客户端调用connect,向服务端发送连接请求报文.该报文是一个特殊报文,报文首部同步位SYN=1,同时确认位ACK=0,seq=x表示确认字段的值为x,该字段值由客户端选择,表示客户端向 ...
-
动态规划 HDU 1176
免费馅饼 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...
-
转:关于rename命令ubuntu下的用法
下面是我的遭遇:上午想批量改几个文件的名字,觉得mv在批量方面不够方便,百度到了rename这个命令,原谅我吧,我总是在百度不到结果时才去看google,以后还是少去百度的好国内很多贴子都在说linu ...
-
MFC实现为窗体添加的背景图片
将一个bmp图片添加到资源中 在资源视图中更改位图资源的ID为IDB_BITMAP_BACKGROUND. 第一种方法: 在Dialog中添加一个Picture Control控件,将Picture ...
-
Python获取会议部分的信息内容(不断完善中)
这是一个用于获取物理师会议报告的简单爬虫,数据库表结构正在不断完善中 爬虫信息: # -*- coding:utf-8 -*- import urllib.request import pymysql ...
-
通过session的id号获取对应的session
说说为什么要用session!!! 每次访问端通过普通http协议访问tomcat时,访问端包括网页或Android app等,tomcat都会自动生成一个不同的session,而且session的i ...
-
[0406]学习一个——Unit 1 Html、CSS与版本控制
前言 最近发现了Github的Student认证,本来想用来注册Digital Ocean搭个*,结果注册验证不能用VISA借记卡=~=. 那么在这漫长的清明节假期里,只有学习能满足空虚的内心(划掉 ...