[ Java学习 ] 线程实验2

题目:





法一:

/* 法一:
 * 客户作为线程,柜台作为链表里的元素,是共享资源
 * 优点:易于模拟客户借贷条件不满足时的停等需求。
 * 缺点:不保证客户是按来的先后次序被服务。
 */
import java.util.*;
public class test
{
	static final int workTime = 5; //银行工作时间,单位是分钟
	static boolean working = true; //银行是否仍在工作
	
	public static void main(String args[])
	{
		solve();
	}
	public static void solve()
	{
		LinkedList<Counter> list = new LinkedList<Counter>(); //可用柜台链表
		Operation o = new Operation(list); //功能类对象
		Deal threads[] = new Deal[10]; //10个用户对象
		String[]s = { "柜台1", "柜台2", "柜台3" };
		Counter ctr[] = new Counter[3];
		Customer c[] = new Customer[10];
		for (int i = 0; i < 3; i++)
		{
			// 生成3个柜台,并添加到可用柜台链表list
			ctr[i] = new Counter(s[i]);
			list.add(ctr[i]);
		}
		System.out.println("客户基本信息为:");
		System.out.println("------------------------------------");
		for (int i = 0; i < 10; i++) //产生10个客户
			c[i] = new Customer();
		
		System.out.println("------------------------------------\n\n");
     	System.out.println("银行开始工作啦!");
     	System.out.println("------------------------------------");
     	
     	for (int i = 0; i < 10; i++)
     	{
     		threads[i] = new Deal(o, c[i]);
     		threads[i].start();
     	}
     	
     	try
     	{
     		Thread.sleep(1000 * 60 * workTime);
     	}
     	catch(InterruptedException e) {}
     	
     	working = false; // 银行下班
     	for (int i = 0; i < 10; i++)
     		threads[i].interrupt();
     	
     	try{ Thread.sleep(3000);} // 缓冲,等待线程结束
     	catch(InterruptedException e) { }
     	
     	System.out.println("\n\n");
    	System.out.println("--------------------------------------");
    	System.out.println("| 银行下班了,各柜台提交今天的处理结果:|");  
    	System.out.println("--------------------------------------");
    	
    	long average = (long)(Operation.totalTime / Customer.sum);
    	System.out.println("今天共有" + Customer.sum + "位客户,处理了" + Operation.customerNum + "位有效客户,10位客户的平均等待时间为:" + average + "毫秒");
    	
    	for (int i = 0; i < 3; i++)
    	{
    		ctr[i].showInfo();
    	}
		System.exit(0); //结束所有线程
	}
}
class Counter //柜台类
{
	private int num[] = new int[4]; //用于记录4中服务的总数
	public String name; //柜台名
	public Counter(String n)
	{
		name = n;
		for (int i = 0; i < 4; i++)
			num[i] = 0;
	}
	public void addnum(int i) //只要传入业务类型所代表的数字,可自增其对应的服务的计数变量
	{
		num[i - 1]++;
	}
	public void showInfo() //柜台的每天各种服务类型的成果的输出
	{
		System.out.println("我是" + name + ";今天成果为:处理业务1(借贷): " + num[0] + "人;处理业务(还贷):" + num[1] + "人;处理业务3(挂失): " + num[2] + "人;处理业务(咨询): " + num[3] + "人");
	}
}
class Customer //客户类
{
	public static String []types = {"", "借贷", "还贷", "挂失", "咨询"};
	public static int sum = 0; // 自动编号,用于记录客户类当前已编号到哪个客户,该数据属于整个类,而不属于某个特定的客户对象
	public int num; // 属于每个客户对象,记录其自身的编号
	public int type; // 业务类型
	public int money; // 该客户此次操作的对应金额 
	public Date time; // 客户到达时间
	public Customer() // 默认构造函数中,为其编号,随机生成业务类型和(如果必要)时的业务金额
	{
		num = ++sum;
		type = (int)(Math.random() * 4 + 1); // 随机产生[1,4]中的整数作为业务类型
		time = new Date();
		if (type == 1 || type == 2)
		{
			money = (int)(Math.random() * 200 + 1); // 随机产生[1,200]中的整数作为借贷还贷金额
			System.out.println("客户编号:" + num + "   客户类型:" + types[type] + "   处理金额:" + money);
		}
	}
}

class Operation //操作类
{
	private int balance = 0; //银行总资产
	static int customerNum = 0; //处理的有效客户数
	static double totalTime = 0; //客户等待的总时间
	private LinkedList<Counter> list; //当前可用柜台组成的链表
	
	public Operation(LinkedList<Counter> l)
	{
		list = l;
	}
	public LinkedList<Counter> getCounters()
	{
		return list;
	}
	public synchronized Counter getCounter(Customer c)//为当前客户线程寻找一个可用柜台
	{
		while (list.size() == 0)
		{
			try
			{
				wait();
			}
			catch(InterruptedException e) {}
		}
		Counter ans = list.getFirst(); // 取柜台队列中的首元素给客户
		list.removeFirst(); // 移除队列元素
		System.out.println("客户" + c.num + "请到" + ans.name + "办理\n");
		return ans; //返回客户办理业务所在的柜台
	}
	public synchronized void solve(Customer c) // 处理具体业务
	{
		Counter ctr = getCounter(c);
		if (c.type == 1) loan(c, ctr); // 借贷
		else if (c.type == 2) repay(c, ctr); // 还贷
		else if (c.type == 3) missing(c, ctr); // 挂失
		else if (c.type == 4) consult(c, ctr); // 咨询
	}
	public synchronized void loan(Customer c, Counter ctr) // 借贷
	{
		boolean notSolved = false; // 判断是否有因为钱不足而未完成的贷款请求
		while (test.working && balance < c.money ) // 如果银行还在上班,并且借贷钱款超过了银行的钱数,则标记变量notSolved置false,将柜台加回可用柜台列表,让出时间片
		{
			try
			{
				System.out.println("客户编号:" + c.num + ":银行资金不足,暂不能满足借贷要求,请等待:");
				System.out.println("----------------------------------------");
				list.addLast(ctr);
				notSolved = true;
				this.wait();
			}
			catch(InterruptedException e) {}
		}
		if (notSolved)
			solve(c); // 如果之前有因为钱不够没被处理的贷款,从wait被唤醒后,需要重新寻找可用柜台,从  solve 函数重新进入 loan 函数
		else
		{
			if (test.working) //如果还仍处于银行营业时间,且此时银行有足够的钱以供借贷,那么直接处理借贷请求
			{
				balance -= c.money;
				ctr.addnum(1); // 第一类操作的总计数更新
				try
				{
					Thread.sleep(1000);
				}
				catch(InterruptedException e) {}
				list.addLast(ctr); // 柜台处理完该客户后,重新加入可用柜台列表
				showTime(true, c, ctr);
			}
			else // 如果银行已关门,且还有尚未处理的借贷
			{
				System.out.println("----------------------------------------");
				System.out.println("客户编号: " + c.num + "说:银行下班了,今天没有借到钱,以后再来");
				showTime(false, c, ctr);
			}
		}
	}
	
	public synchronized void repay(Customer c, Counter ctr) // 还贷
	{
		balance += c.money;
		try
		{
			Thread.sleep(3000);
		}
		catch(InterruptedException e) {}
		list.addLast(ctr); // 柜台处理完该客户后,重新加入可用柜台列表
		ctr.addnum(2); // 第二类操作的总计数更新
		showTime(true, c, ctr);
	}
	
	public synchronized void missing(Customer c, Counter ctr) // 挂失
	{
		try
		{
			Thread.sleep(4000);
		}
		catch(InterruptedException e) {}
		list.addLast(ctr);// 柜台处理完该客户后,重新加入可用柜台列表
		ctr.addnum(3);// 第三类操作的总计数更新
		showTime(true, c, ctr);
	}
	
	public synchronized void consult(Customer c, Counter ctr) // 咨询
	{
		try
		{
			Thread.sleep(5000);
		}
		catch(InterruptedException e) {}
		list.addLast(ctr);// 柜台处理完该客户后,重新加入可用柜台列表
		ctr.addnum(4);// 第四类操作的总计数更新
		showTime(true, c, ctr);
	}
	
	public synchronized void showTime(boolean endAll, Customer c, Counter ctr)
	{
		Date  endTime = new Date(); // 客户离开的时间
		double lastTime = (endTime.getTime() - c.time.getTime()); // 表示持续时间,last翻译为持续,而不是上一个
		Operation.totalTime += lastTime;
		
		if (endAll)
		{
			Operation.customerNum++;
			
		}
		else
		{
			System.out.println("未满足服务的客户信息:");
		}
		System.out.println(ctr.name + "说:客户编号:" + c.num + ",  业务类型(" + c.types[c.type] + "),  到的时间为:" + c.time + ",  走的时间为:" + endTime + ",  逗留时间为(毫秒):" + lastTime);
		System.out.println("----------------------------------------");
		notifyAll();
	}
}

class Deal extends Thread
{
	Operation o;
	Customer c;
	public Deal(Operation _o, Customer _c)
	{
		o = _o;
		c = _c;
	}
	public void run()
	{
		o.solve(c);
	}
}


法二:

/* 法二:
 * 以柜台作为线程,客户作为共享资源。
 * 为了简化起见,当客户借贷要求不满足时,站到队列尾重新排队。
 * 这种做法的好处是客户是按到达次序处理的;缺点是当借贷条件不满足时,无法使用wait方法
 */
import java.util.*;
public class test
{
	static final int workTime = 5; //银行工作时间,单位是分钟
	static boolean working = true; //银行是否仍在工作
	public static void main(String args[])
	{
		solve();
	}
	public static void solve()
	{
		LinkedList<Customer> cus = new LinkedList<Customer>(); //可用客户链表
		Operation o = new Operation(cus); //功能类对象
		Deal threads[] = new Deal[3]; //3个柜台对象
		String[]s = { "柜台1", "柜台2", "柜台3" };
		Counter ctr[] = new Counter[3];
		
		for (int i = 0; i < 3; i++)
			ctr[i] = new Counter(s[i]);
		
		System.out.println("客户基本信息为:"); 
    	System.out.println("------------------------------------");
    	
    	for (int i = 0; i < 10; i++)
    		cus.add(new Customer());
    	
    	System.out.println("------------------------------------\n\n");
     	System.out.println("银行开始工作啦!");
     	System.out.println("------------------------------------");
     	
     	for (int i = 0; i < 3; i++)
     	{
     		threads[i]= new Deal(o, ctr[i]);
     		threads[i].start();
     	}

     	try
     	{
     		Thread.sleep(1000 * 60 * workTime);
     	}
     	catch(InterruptedException e) {}
     	
     	working = false; // 银行下班
     	
     	Date  endTime = new Date();
     	double lastTime = 0; //每个客户从到来到离开的持续时间
     	Iterator <Customer> it = o.getWaiting().iterator(); //下班后,先把没借到钱的客户,也即等待队列里的客户的有关信息输出
     	while (it.hasNext())
     	{
     		Customer now = it.next(); //now表示现在在处理的客户
     		System.out.println("--------------------------------------");
     		System.out.println("客户编号:" + now.num + "说:银行下班了,今天没有借到钱,以后再来");
     		lastTime = endTime.getTime() - now.time.getTime();
     		Operation.totalTime += lastTime;
     	}
     	
     	for (int i = 0; i < 3; i++)
     		threads[i].interrupt();
     	
     	try
     	{
     		Thread.sleep(1000);
     	} //缓冲,等待线程结束
     	catch(InterruptedException e) {}
     	
     	System.out.println("\n\n");
    	System.out.println("--------------------------------------");
    	System.out.println("| 银行下班了,各柜台提交今天的处理结果:|");  
    	System.out.println("--------------------------------------");
    	
    	long average = (long)(Operation.totalTime / Customer.sum);
    	System.out.println("今天共有" + Customer.sum + "位客户,处理了" + Operation.customerNum + "位有效客户,10位客户的平均等待时间为:" + average + "毫秒");
     	
    	for (int i = 0; i < 3; i++)
    	{
    		ctr[i].showInfo();
    	}
		System.exit(0); //结束所有线程
	}
}
class Counter //柜台类
{
	private int num[] = new int[4]; //用于记录4中服务的总数
	public String name; //柜台名
	public Counter(String n)
	{
		name = n;
		for (int i = 0; i < 4; i++)
			num[i] = 0;
	}
	public void addnum(int i) //只要传入业务类型所代表的数字,可自增其对应的服务的计数变量
	{
		num[i - 1]++;
	}
	public void showInfo() //柜台的每天各种服务类型的成果的输出
	{
		System.out.println("我是" + name + ";今天成果为:处理业务1(借贷): " + num[0] + "人;处理业务(还贷):" + num[1] + "人;处理业务3(挂失): " + num[2] + "人;处理业务(咨询): " + num[3] + "人");
	}
}
class Customer //客户类
{
	public static String []types = {"", "借贷", "还贷", "挂失", "咨询"};
	public static int sum = 0; // 自动编号,用于记录客户类当前已编号到哪个客户,该数据属于整个类,而不属于某个特定的客户对象
	public int num; // 属于每个客户对象,记录其自身的编号
	public int type; // 业务类型
	public int money; // 该客户此次操作的对应金额 
	public Date time; // 客户到达时间
	public Customer() // 默认构造函数中,为其编号,随机生成业务类型和(如果必要)时的业务金额
	{
		num = ++sum;
		type = (int)(Math.random() * 4 + 1); // 随机产生[1,4]中的整数作为业务类型
		time = new Date();
		if (type == 1 || type == 2)
		{
			money = (int)(Math.random() * 200 + 1); // 随机产生[1,200]中的整数作为借贷还贷金额
			System.out.println("客户编号:" + num + "   客户类型:" + types[type] + "   处理金额:" + money);
		}
	}
}

class Operation //操作类
{
	private int balance = 0; //银行总资产
	static int customerNum = 0; //处理的有效客户数
	static double totalTime = 0; //客户等待的总时间
	private LinkedList<Customer> solving; //正在排队的客户所用列表
	private LinkedList<Customer> waiting = new LinkedList<Customer>(); //不满足借贷条件时,用户的等待链表
	
	public Operation(LinkedList<Customer> s)
	{
		solving = s;
	}
	public LinkedList<Customer> getSolving() //返回服务链表
	{
		return solving;
	}
	public LinkedList<Customer> getWaiting() //返回等待链表
	{
		return waiting;
	}
	public synchronized Customer getCustomer(Counter ctr) //为当前柜台选择一个客户
	{//为柜台选择一个客户
		while (solving.size() == 0)
		{ //若客户链表为空,说明顾客暂时已处理完,则应等待
			try
			{
				wait();
			}catch(InterruptedException e) {}
		}
		Customer c = solving.getFirst();
		solving.remove(c);
		System.out.println("客户" + c.num + "请到" + ctr.name + "办理\n");
		notifyAll();
		return c;
	}
	public synchronized void solve(Counter ctr)
	{
		Customer c = getCustomer(ctr);
		if (c.type == 1) loan(c, ctr); // 借贷
		else if (c.type == 2) repay(c, ctr); // 还贷
		else if (c.type == 3) missing(c, ctr); // 挂失
		else if (c.type == 4) consult(c, ctr); // 咨询
	}
	public synchronized void loan(Customer c, Counter ctr) // 借贷
	{
		if (balance < c.money)
		{
			System.out.println("客户编号:" + c.num + ":银行资金不足,暂不能满足借贷要求,请等待:");
			System.out.println("-------------------------------------");
			waiting.addLast(c);
		}
		else
		{
			balance -= c.money;
			try
			{
				Thread.sleep(2000);
			}catch(InterruptedException e) {}
			ctr.addnum(1);
			showTime(c, ctr);
		}
	}
	public synchronized void repay(Customer c, Counter ctr) // 还贷
	{
		balance += c.money;
		try
		{
			Thread.sleep(3000);
		}catch (InterruptedException e) {}
		ctr.addnum(2);
		
		showTime(c, ctr);
		//调用showTime()后,也会调用notifyAll();
		
		for (int i = 0; i < waiting.size(); i++) //如果等待的客户链表里,有些借贷请求在该客户还款后,可以被满足了,则处理这个可被满足的客户的贷款请求
		{
			Customer temp = waiting.get(i);
			if (balance > temp.money)
			{
				waiting.remove(temp);
				loan(temp, ctr);
			}
		}
	}
	public synchronized void missing(Customer c, Counter ctr) // 挂失
	{
		try
		{
			Thread.sleep(4000);
		}catch(InterruptedException e) {}
		ctr.addnum(3);
		showTime(c, ctr);
	}
	public synchronized void consult(Customer c, Counter ctr) // 咨询
	{
		try
		{
			Thread.sleep(5000);
		}catch(InterruptedException e) {}
		ctr.addnum(4);
		showTime(c, ctr);
	}
	public synchronized void showTime(Customer c, Counter ctr)
	{
		Date  endTime = new Date(); // 客户离开的时间
		double lastTime = (endTime.getTime() - c.time.getTime()); // 表示持续时间,last翻译为持续,而不是上一个
		Operation.totalTime += lastTime;
		
		Operation.customerNum++;
		System.out.println(ctr.name + "说:客户编号:" + c.num + ",  业务类型(" + c.types[c.type] + "),  到的时间为:" + c.time + ",  走的时间为:" + endTime + ",  逗留时间为(毫秒):" + lastTime);
		System.out.println("----------------------------------------");
		notifyAll();
	}
}

class Deal extends Thread
{
	Operation o;
	Counter ctr;
	
	public Deal(Operation _o, Counter _ctr)
	{
		o = _o;
		ctr = _ctr;
	}
	public void run()
	{
		while (test.working)
			o.solve(ctr);
	}
}

心得体会:

  这次的实验做得可谓十分艰难,一直磕磕碰碰,不会时,翻了很久的书,和老师以前给的PPT里的例子代码,各种查阅资料和问同学,最终还是把这两道题写完了...不过也整整从周四写到了周末,写了特别特别久,算是做Java实验做得最久的一次了,史无前例地久...如果还算上,在开始实验之前,还把书上Java的例程敲了一次,这么一算,我觉得应该可以说写了四五天之久。不过老师也确实表示了,这题的确有难度,实在做不出也没关系,..然而能做出来还是特别开心,感觉用了那么久时间也是完全值得的!~

 

  我觉得这题的难度主要倒不是知识点或者语言上的难度,那些难度其实只要网上搜索就能立刻解决的。而这题的难度,则是难在如何使代码十分有系统性,并且尽量提高代码的重用性,等等... 最难处理的是细节,因为这题的细节不仅繁多,而且经常测试的时候才突然冒出来,于是又要打断现有的思路,去考虑和寻找问题究竟出在哪里。如果要完全独立,不和任何人讨论,从头到尾完全自己写出来,我觉得不仅仅相当有难度,而且要用很久时间...可能是我现在的水平比较低吧...

 

  不过不管怎么说,别管究竟是讨论了多久,借鉴了老师的PPT例程里类似的写法借鉴了多少,反正至少做出来了嘛!~All's well that ends well...虽然确实历时很久,说明自己基础不够,水平还是很低,但是...至少最终做出来了,我对这个结果就还是比较满意的!~

 

查阅的链接(都是超链接,可直接点击)

多线程总结

 random()方法获得一定范围内的随机数

java多线程小结,及解决应用挂死的问题

Linked List 链表详解

 

然而正如前述,这题并不是难在知识点,而是难在不太容易一次想得很全面,在做的过程中,也需要不断修补思路,改进细节,很消磨我的耐心...所以,其实查过的资料反而没那么多了

 

一句话总结一下:目测是值得重做,并且每次重做大概都会有新体会的一道题


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值