【ThinkingInJava】64、银行出纳员仿真

/**
* 书本:《Thinking In Java》
* 功能:对象随机地出现,并且要求由数量有限的服务器提供随机数量的服务时间。
* 		每个银行顾客要求一定数量的服务时间,这是出纳员必须花费在顾客身上,以服务顾客需求的时间单位的数量。服务时间的数量对每个顾客来说都是不同的,并且是随机确定的。
* 		另外,你不知道在每个时间间隔内有多少顾客会到达,因此这也是随机确定的:
* 文件:BankTellerSimulation.java
* 时间:2015年5月10日08:36:06
* 作者:cutter_point
*/
package Lesson21Concurency;

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(int serviceTime)
	{
		this.serviceTime = serviceTime;
	}
	public int getServiceTime()
	{
		return serviceTime;
	}
	@Override
	public String toString()
	{
		return "顾客要求的  [服务时间 =" + serviceTime + "]";
	}
}

//顾客队列
/*
 * 一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。
 * 新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。 
这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。
试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
 */
class CustomerLine extends ArrayBlockingQueue<Customer>
{
	public CustomerLine(int maxLineSize)	//数组的最大长度
	{
		super(maxLineSize);
	}

	@Override
	public String toString()
	{
		if(this.size() == 0)
			return "[没有顾客,为空]";
		StringBuilder result = new StringBuilder();
		for(Customer customer : this)
		{
			//吧数组里面的顾客全部取出来,放到string里面去
			result.append(customer);
		}
		return result.toString();
	}
}

//顾客产生器,随机产生顾客
class CustomerGenerator implements Runnable
{
	//得到顾客的队列
	private CustomerLine customers;
	//产生随机数
	Random rand = new Random(998);
	
	public CustomerGenerator(CustomerLine customers)
	{
		this.customers = customers;
	}

	@Override
	public void run()
	{
		try
		{
			//只要当前线程没有被打断,那么我们就不断地产生新的顾客
			while(!Thread.interrupted())
			{
				//为了随机时间里面产生顾客,产生的顾客需求也是随机地
				//随机一段时间 nextInt是产生一个在0到指定的数据之间的数
				TimeUnit.MILLISECONDS.sleep(rand.nextInt(300));
				//然后队列里面添加一个随机地顾客
				customers.put(new Customer(rand.nextInt(1000)));
			}
		} 
		catch (InterruptedException e)
		{
			System.out.println("顾客产生器线程被打断了");
			e.printStackTrace();
		}
		System.out.println("顾客产生器 终结(terminating)");
	}
}

//银行出纳员类
class Teller implements Runnable, Comparable<Teller>
{
	private static int count = 0;	//计数,银行有几个出纳员
	private final int id = count++;	//当前出纳员的员工id号
	private int customersServed = 0;		//当前出纳员服务了多少顾客
	private CustomerLine customers;		//顾客队列
	private boolean servingCustomerLine = true;		//当前是否有顾客队列

	public Teller(CustomerLine customers)
	{
		this.customers = customers;
	}
	
	//当没有顾客的时候,做点其他的事
	public synchronized void doSomethingElse()
	{
		//吧服务过的顾客数修正为0
		customersServed = 0;
		//并把当前的服务队列改为没有队列
		servingCustomerLine = false;
	}
	
	//当出现许多顾客的时候,该上班做事了
	public synchronized void serveCustomerLine()
	{
		//来个断言assertion机制,判定已经服务了多少人
		assert !servingCustomerLine : " 已经服务了: " + this;	//判定!servingCustomerLine是不是为false,如果不是那就用第二个String作为产生,抛出异常
		servingCustomerLine = true;		//重新把队列启动
		//唤醒其他说有的工作
		this.notifyAll();
	}

	//选择当前类服务过的顾客最小的出纳员进行工作
	@Override
	public synchronized int compareTo(Teller o)
	{
		return customersServed < o.customersServed ? -1 : (customersServed == o.customersServed ? 0 : 1);
	}

	@Override
	public void run()
	{
		try
		{
			//首先这个线程没有被中断
			while(!Thread.interrupted())
			{
				//然后队列里面排第一个的顾客脱离队伍,接受服务
				Customer customer = customers.take();
				//这个要被服务的时间队列需要停顿
				TimeUnit.MILLISECONDS.sleep(customer.getServiceTime());
				//然后给这个出纳员的服务数量添加一个
				synchronized(this)
				{
					customersServed++;
					while(!servingCustomerLine)	//当队列没有人的时候
						this.wait();
				}
			}
		} 
		catch (InterruptedException e)
		{
			System.out.println(this + "被中断");
			e.printStackTrace();
		}
	}

	@Override
	public String toString()
	{
		return "出纳员工号是 : [id=" + id + "]";
	}
	
	public String shortString() { return "员工号 : " + id; }
}

//银行管理类
class TellerManager implements Runnable
{
	private ExecutorService exec;	//线程连接池
	private CustomerLine customers;	//顾客队列
	//一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,
	private PriorityQueue<Teller> workingTellers = new PriorityQueue<Teller>();
	private Queue<Teller> tellerDoingOtherThings = new LinkedList<Teller>();	//没有加入工作的出纳员
	private Random rand = new Random(998);
	private int adjustmentPeriod;	//调整期间
	
	public TellerManager(ExecutorService exec, CustomerLine customers, int adjustmentPeriod)
	{
		this.exec = exec;
		this.customers = customers;
		this.adjustmentPeriod = adjustmentPeriod;
		//首先银行最开始是一个出纳员工作,开始分配任务
		Teller teller = new Teller(customers);
		//投入工作
		exec.execute(teller);
		//工作的人加一个人
		workingTellers.add(teller);		
	}
	
	//这个函数用来调整工作的出纳员的人数,以及是否需要招聘
	public void adjustTellerNumber()
	{
		//如果顾客队列比出纳员的人数的两倍还要多,注意是队列,所以我们得加一些出纳员进行工作了
		if(customers.size() / workingTellers.size() > 2)
		{
			//首先,我们得确定公司里面还有一些人没有做事,在休息
			if(tellerDoingOtherThings.size() > 0)
			{
				//吧那些没有上班的人全部拉上来去做事
				Teller teller = tellerDoingOtherThings.remove();		//吧头部元素拿出来
				teller.serveCustomerLine(); //玩你妈比,起来做事
				workingTellers.offer(teller);	//给工作队列加入新的血液
				return;
			}
			//如果休息的员工没了,全都进行了工作,那么就得招点人手了
			Teller teller = new Teller(customers);
			exec.execute(teller); 	//启动线程
			workingTellers.add(teller);
			return;
		}
		
		//当然,生意不可能一直这么火爆
		if(workingTellers.size() > 1 && customers.size() / workingTellers.size() < 2)
		{
			this.reassignOneTeller(); //休假去
		}
		
		//当完全没有顾客的时候
		if(customers.size() == 0)
		{
			this.reassignOneTeller(); //休假去
		}
	}
	
	private void reassignOneTeller()
	{
		Teller teller = workingTellers.poll();		//去掉头部元素
		teller.doSomethingElse(); //休息去吧
		tellerDoingOtherThings.offer(teller); //休假人数+1
	}

	@Override
	public void run()
	{
		try
		{
			//当银行没有倒闭的时候
			while(!Thread.interrupted())
			{
				TimeUnit.MILLISECONDS.sleep(adjustmentPeriod); //调整一段时间先
				this.adjustTellerNumber(); 	//调整员工
				System.out.println(customers + " { "); //顾客队列
				for(Teller teller : workingTellers)
					System.out.println(teller.shortString() + " "); 	//为队列工作
				System.out.println(" } ");
			}
		} 
		catch (InterruptedException e)
		{
			System.out.println("好样的!银行倒闭了");
			e.printStackTrace();
		}
		System.out.println("银行业务 终结(terminating)");
	}
	public String toString() { return "这是一家银行"; }
}

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));
		for(int i = 0; i < 5; ++i)
		{
			System.out.println("回车,让银行破产!!!!!!!!");
		}
		System.in.read();
		exec.shutdownNow(); 	//停止一切线程		
	}
}

输出:

回车,让银行破产!!!!!!!!
回车,让银行破产!!!!!!!!
回车,让银行破产!!!!!!!!
回车,让银行破产!!!!!!!!
回车,让银行破产!!!!!!!!
顾客要求的  [服务时间 =91]顾客要求的  [服务时间 =385]顾客要求的  [服务时间 =437]顾客要求的  [服务时间 =191]顾客要求的  [服务时间 =572] {
员工号 : 1
员工号 : 0
 }
顾客要求的  [服务时间 =572]顾客要求的  [服务时间 =324]顾客要求的  [服务时间 =150]顾客要求的  [服务时间 =793]顾客要求的  [服务时间 =452]顾客要求的  [服务时间 =853]顾客要求的  [服务时间 =849]顾客要求的  [服务时间 =743]顾客要求的  [服务时间 =334] {
员工号 : 2
员工号 : 0
员工号 : 1
 }
顾客要求的  [服务时间 =743]顾客要求的  [服务时间 =334]顾客要求的  [服务时间 =517]顾客要求的  [服务时间 =608]顾客要求的  [服务时间 =5]顾客要求的  [服务时间 =943]顾客要求的  [服务时间 =701]顾客要求的  [服务时间 =187]顾客要求的  [服务时间 =880] {
员工号 : 3
员工号 : 2
员工号 : 1
员工号 : 0
 }
顾客要求的  [服务时间 =880]顾客要求的  [服务时间 =757]顾客要求的  [服务时间 =992]顾客要求的  [服务时间 =431]顾客要求的  [服务时间 =923]顾客要求的  [服务时间 =273]顾客要求的  [服务时间 =716] {
员工号 : 2
员工号 : 0
员工号 : 1
 }
顾客要求的  [服务时间 =273]顾客要求的  [服务时间 =716]顾客要求的  [服务时间 =305]顾客要求的  [服务时间 =110]顾客要求的  [服务时间 =926]顾客要求的  [服务时间 =783]顾客要求的  [服务时间 =585]顾客要求的  [服务时间 =655] {
员工号 : 2
员工号 : 0
员工号 : 1
 }
顾客要求的  [服务时间 =585]顾客要求的  [服务时间 =655]顾客要求的  [服务时间 =809]顾客要求的  [服务时间 =835]顾客要求的  [服务时间 =314]顾客要求的  [服务时间 =10] {
员工号 : 2
员工号 : 0
员工号 : 1
 }
顾客要求的  [服务时间 =314]顾客要求的  [服务时间 =10]顾客要求的  [服务时间 =769]顾客要求的  [服务时间 =372]顾客要求的  [服务时间 =614]顾客要求的  [服务时间 =402]顾客要求的  [服务时间 =589]顾客要求的  [服务时间 =38]顾客要求的  [服务时间 =283] {
员工号 : 3
员工号 : 2
员工号 : 1
员工号 : 0
 }
顾客要求的  [服务时间 =38]顾客要求的  [服务时间 =283]顾客要求的  [服务时间 =176]顾客要求的  [服务时间 =283]顾客要求的  [服务时间 =553]顾客要求的  [服务时间 =330]顾客要求的  [服务时间 =416]顾客要求的  [服务时间 =21]顾客要求的  [服务时间 =799]顾客要求的  [服务时间 =796]顾客要求的  [服务时间 =89] {
员工号 : 3
员工号 : 2
员工号 : 1
员工号 : 0
 }
顾客要求的  [服务时间 =357]顾客要求的  [服务时间 =483]顾客要求的  [服务时间 =358]顾客要求的  [服务时间 =136]顾客要求的  [服务时间 =435] {
员工号 : 2
员工号 : 0
员工号 : 1
 }

顾客产生器线程被打断了
好样的!银行倒闭了
出纳员工号是 : [id=2]被中断
出纳员工号是 : [id=3]被中断
出纳员工号是 : [id=1]被中断
出纳员工号是 : [id=0]被中断
java.lang.InterruptedException: sleep interrupted
顾客产生器 终结(terminating)
银行业务 终结(terminating)
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:360)
    at Lesson21Concurency.Teller.run(BankTellerSimulation.java:157)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:360)
    at Lesson21Concurency.Teller.run(BankTellerSimulation.java:157)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:360)
    at Lesson21Concurency.CustomerGenerator.run(BankTellerSimulation.java:92)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:360)
    at Lesson21Concurency.TellerManager.run(BankTellerSimulation.java:257)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:360)
    at Lesson21Concurency.Teller.run(BankTellerSimulation.java:157)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:360)
    at Lesson21Concurency.Teller.run(BankTellerSimulation.java:157)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)











郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。