题目:
法一:
/* 法一:
* 客户作为线程,柜台作为链表里的元素,是共享资源
* 优点:易于模拟客户借贷条件不满足时的停等需求。
* 缺点:不保证客户是按来的先后次序被服务。
*/
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...虽然确实历时很久,说明自己基础不够,水平还是很低,但是...至少最终做出来了,我对这个结果就还是比较满意的!~
查阅的链接(都是超链接,可直接点击)
然而正如前述,这题并不是难在知识点,而是难在不太容易一次想得很全面,在做的过程中,也需要不断修补思路,改进细节,很消磨我的耐心...所以,其实查过的资料反而没那么多了
一句话总结一下:目测是值得重做,并且每次重做大概都会有新体会的一道题