多线程一个很有意思的作用就是用于仿真,这篇博客就会结合几个仿真实例来综合运用一下前面所学的多线程并发知识。
一.银行出纳员仿真
问题描述:银行会有很多来办业务的顾客,他们会排队等待服务;对于银行方面他们派出出纳员来服务顾客,如果排队的顾客数量过多,银行就会增加
出纳员的数量,如果顾客的数目过少,则减少出纳员的数目;总之要保持一个平衡。
仿真思路:封装Customer类来表示顾客,每个顾客对象都会有一个需要服务的时间;使用有限容量的阻塞队列CustomerLine来模拟顾客的排队队列;封装
CustomerGenerator类来产生顾客,然后将产生的顾客加入到CustomerLine中去;封装Teller类来表示银行的出纳员,Teller会从CustomerLine中取出;
Customer来进行服务。封装TellerManage来管理所有的Teller及根据顾客/出纳员的比例来调整服务顾客的Teller数量。在这里我们通过阻塞队列CustomerLine实现了Teller线程和CustomerGenerator线程之间的通信。
具体的实现代码如下:
package lkl;
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;
/**
* 多线程模拟银行出纳员问题
* */
//模拟顾客类,完全只是一个可读类,不需要同步
class Customer{
//该顾客所需服务时间
private final int serviceTime;
public Customer(final int serviceTime){
this.serviceTime = serviceTime;
}
public int getServiceTime(){
return serviceTime;
}
public String toString(){
return "["+serviceTime+"]";
}
}
//模拟顾客排队的队列,继承了阻塞队列
//是一个多线程共享对象,这个队列继承的是ArrayBlocingQueue
//是一个有最大长度的队列
class CustomerLine extends ArrayBlockingQueue<Customer>{
//指定允许队列的最大长度
public CustomerLine(int maxSize){
super(maxSize);
}
//重写toString()方法,用来进行显示当前排队中的顾客
public String toString(){
if(this.size()==0)
return "[Empty]";
StringBuilder result = new StringBuilder();
for(Customer customer :this){
result.append(customer);
}
return result.toString();
}
}
//顾客生产类
//间隔随机然后向队列中添加一位顾客的线程
class CustomerGenerator implements Runnable{
private CustomerLine customerLine; //阻塞队列
private static Random rand = new Random(47);
public CustomerGenerator(CustomerLine customerLine){
this.customerLine = customerLine;
}
public void run(){
try{
while(!Thread.interrupted()){
//线程睡眠随机时间以后,产生一个顾客对象,添加到队列中
TimeUnit.MILLISECONDS.sleep(rand.nextInt(300));
//添加一个服务时间随机的顾客
customerLine.add(new Customer(rand.nextInt(1000)));
}
}catch(InterruptedException ex){
System.out.println(this+" 通过中断异常退出");
}
System.out.println(this+" terminating");
}
}
//出纳员类,负责对队列中的顾客进行服务
//注意其有两种状态:服务顾客或做一些其它的事情
class Teller implements Runnable,Comparable<Teller>{
private static int counter = 0;
private final int id = counter++;
//该Teller服务的顾客队列
private CustomerLine customerLine;
private int customerServed = 0;//已服务的顾客数
//标志目前是被分配到服务CustomerLine还是做一些其它事
//默认是分配给customerLine
private boolean servingCustomerLine=true;
public Teller(CustomerLine cl){
this.customerLine = cl;
}
//正常情况下会从CustomerLine中取出一个Customer进行服务
//如果被分配到做其它事,则会被挂起
public void run(){
try{
while(!Thread.interrupted()){
Customer customer = customerLine.take();
//睡眠一段时间模拟服务Customer
TimeUnit.MILLISECONDS.sleep(customer.getServiceTime());
synchronized(this){
while(!servingCustomerLine){
//被分配做其它事情
wait();
}
}
}
}catch(InterruptedException ex){
System.out.println(this+"通过中断异常退出");
}
System.out.println(this+"Terminating");
}
//调用这个方法意味着该Teller对象被分配去做其它事情
public synchronized void doSomethingElse(){
customerServed = 0;
servingCustomerLine=false; //设定标志,是当前服务线程挂起
}
//被分配到服务到customerLine
public synchronized void serveCustomerLine(){
servingCustomerLine = true;
notifyAll();//通知挂起线程
}
public String toString(){
return "Teller "+id+" ";
}
public String shortString(){
return "T "+id;
}
//按以服务顾客数确定Teller的优先级,给优先队列使用
@Override
public synchronized int compareTo(Teller other){
return customerServed < other.customerServed ? -1:
(customerServed==other.customerServed ? 0 :1);
}
}
//服务管理和调度Teller的类
//这个TellerManager类是各种活动的中心,它跟踪所有的出纳员以及等待服务的顾客
//从adjustTellerNumber()中可以看到,它会根据实际情况调整服务CustomerLine的
//Teller数量,以期达到最优出纳员的数目。
class TellerManager implements Runnable{
private ExecutorService exec; //负责启动Teller线程
private CustomerLine customerLine;
//按服务顾客数由少到多优先的优先队列,用来进行调度
//每次都取出服务顾客数最少的出纳员来进行服务,