多线程解决这道题是很好,但是考虑到是面试题,假设完成时间是1个小时,那么面试官测试这个程序,岂不是要等1个小时,才能等到结果啊~大家有没有想到静态的方法。输入的是一个文件,文件的内容像 2 (收银台的数量) A 1 5 B 2 1 A 3 5 B 5 3 A 8 2所以这个顾客数是一定的,收银台数是一定的,只需要调用就能使用。 能不能根据顾客的属性,去选择哪个收银台,比如 到达时间,处理商品时间,完成时间,这几个变量去判断哪个收银台,以及完成的总时间 就可以不用线程去实现。大家觉得怎么样呢,给点建议吧。谢啦~
step1,把收银台何顾客都封装成对象。
step2,收银台对象中:list集合用来存储顾客对象。
顾客对象中:有一个time属性,用来存储结账的时间。
有一个chose()方法,用来选择收银台。
step3,创建N个收银台,M个顾客。设置好属性。把这些顾客安排到收银台的list中。
step4,计算每个收银台list中顾客的结账时间,最后把N个收银台的结账时间相加。完成
谢谢你的思路,很好。
我有几个问题
1. 需不需要监测每个时间,收银台的状态。因为每个顾客都有到达的时间,这时应该进行chose哪个收银台。问题是,如何知道每个收银台每分钟的状态。很有可能每分钟都有顾客到达等待结账。
2. 顾客是不是不需要有个 list 了,直接chose哪个收银台就好?
3, 如何显示N个收银台的同步性啊,如果不用线程的话。这个几个收银台是同时在给顾客结账的。谢谢~
那请问不用线程怎么实现啊?顾客如何才能知道每个收银台的状态,有多少人离开,多少人在结账啊?谢谢不用线程怎么实现并发?
难不成你多个收银台还不能同时结帐?
运用线程后,如何去查看每一个收银台的状态,比如队里有多少个人,还有如何查看队里最后一个人的商品数?
能不能给点具体点思路和建议啊~一个收银台对应一个list和一个线程就好了啊,线程相当于现实中的收银员,list相当于收银台前的队就好了。你有几个收银台就开几个线程。这么简单的问题怎么会给整那么复杂
由于本人是菜鸟级别,希望不要被大神鄙视我的难点是
1. 假如一个顾客在t=10 的时候到达,他如何检查 这N个收银台中每一个收银台(1, 2, 3 ...n) 在此刻 队伍里的人数, 然后再去选择去不同的收银台。
2. 有N个收银台,M个顾客。 在main函数中,需要用 for 循环 分别实例化这N个收银台吗? 我想的是因为顾客要去check 每一个收银台的 在他到达时的状态,如果只是实例化一次,不知道该如何实现。难点主要就是 顾客如何检查每个收银台的状态,再去做决定进入哪个队列。谢谢大神~
收银台的建模思路:收银台有普通和培训之分,因此存在一个属性type(可以做成int型,也可为枚举);当前收银台可以接收顾客排队,因此需要一个Queue<顾客> customQueue属性,存放正则排队的顾客;顾客会判断当前收银台最后一个顾客商品数,因此需要一个numOfGoods属性;判断收银台排队人数,通过customQueue.size()即可;为了区分,再增加一个收银台编号,使用字符串。
这样,收银台的数据模型可以是这样的:/**
* 收银台的抽象
*/
public class CashDesk { // 收银台类型
private DeskType type; // 编号
private String name; // 排队的顾客
private Queue<Customer> customQueue; // 队列尾的货物数
private int numOfGoods; // 构造方法,必须输入收银台种类和编号
public CashDesk(DeskType type, String name) {
this.type = type;
this.name = name; // 顾客队列、队尾货物数初始化
customQueue = new LinkedBlockingQueue<Customer>();
numOfGoods = 0;
} // 顾客来了的方法
public void customersComing(Customer... customers) {
// 不能为空
if (null == customers || 0 == customers.length) {
System.out.println("没有顾客结账...");
return;
}
// 加入队列
customQueue.addAll(Arrays.asList(customers));
// 修改队尾顾客货物数
numOfGoods = customers[customers.length - 1].getNumOfGoods();
} // 处理队列中第一位顾客的结账
public void processFirstCustomerCash() {
if (customQueue.isEmpty()) {
return;
} // 获取第一个顾客
Customer firstOne = customQueue.poll();
// 处理他的货物
do {
try {
// 收银台分类
switch (type) {
case Normal: {
Thread.sleep(60000);// 1min
break;
}
case Practice: {
Thread.sleep(120000);// 2min
}
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} // 打印
System.out.println(name + ":" + "搞定顾客" + firstOne.getNumber() + "的1个货物。"); // 若当前顾客为队列最后一个顾客,则需要实时更新队尾货物数
if (customQueue.isEmpty()) {
numOfGoods = firstOne.getNumOfGoods();
}
} while (firstOne.deelNumOfGoods()); // 若队列空了,则修改最后顾客货物数
if (customQueue.isEmpty()) {
numOfGoods = 0;
}
} // 判断排队人数是否为空
public boolean queueIsEmpty() {
return customQueue.isEmpty();
} // 获取当前排队人数
public int getQueueSize() {
return customQueue.size();
} // getter、setter方法
public int getNumOfGoods() {
return numOfGoods;
} public DeskType getType() {
return type;
} public String getName() {
return name;
} // 私有枚举,区分收银台种类
public static enum DeskType {
Normal, Practice
}
}顾客的建模思路:顾客也有分类,存在一个属性type(同上);顾客持有的货物数,numOfGoods属性;实现如下:/**
* 顾客的抽象
*/
public class Customer { // 顾客种类
private CustomType type; // 顾客编号
private int number; // 持有货物数
private int numOfGoods; // 构造方法,必须传入顾客种类、编号和货物数
public Customer(CustomType type, int number, int numOfGoods) {
// 货物数不能小于等于0
if (numOfGoods <= 0) {
throw new IllegalArgumentException("货物数不能小于0!");
}
this.type = type;
this.numOfGoods = numOfGoods;
this.number = number;
} // 持有货物数-1
public boolean deelNumOfGoods() {
numOfGoods--;
return numOfGoods > 0;
} public int getNumOfGoods() {
return numOfGoods;
} public CustomType getType() {
return type;
} public int getNumber() {
return number;
} // 顾客种类枚举
public enum CustomType {
A, B
}
}参考实际收银台的运行,多线程并发执行是比较好的选择。这里的多线程不妨假设为收银员的收银处理,实现如下:/**
* 收银员收银处理线程
*/
public class CashProcessor implements Runnable { // 收银台
private CashDesk cashDesk;
// 构造方法需要初始化收银台
public CashProcessor(CashDesk cashDesk) {
if (null == cashDesk) {
throw new IllegalArgumentException("收银台不能为空!");
}
this.cashDesk = cashDesk;
} /**
* 匹配条件4
* 顾客来了,考虑到可能会有多个顾客,使用数组参数
*/
public void customersComing(Customer... customers) {
if (null == customers || 0 == customers.length) {
System.out.println("没有顾客前来结账...");
return;
} // 为了处理这些顾客排队问题,先进行排序处理// 排序的算法这里就简单的冒泡了
Customer tmp;
for (int i = 0; i < customers.length; i++) {
for (int j = customers.length; j > i; j--) {
// 如果两个顾客的在同一时间到达 收银台, 商品数少的顾客先结账, 如果商品数一样,则A类顾客先于B类
if (customers[j].getNumOfGoods() < customers[j - 1].getNumOfGoods()
|| customers[j].getNumOfGoods() == customers[j - 1].getNumOfGoods()
&& customers[j].getType() == CustomType.A
&& customers[j - 1].getType() == CustomType.B) {
tmp = customers[j];
customers[j] = customers[j - 1];
customers[j - 1] = tmp;
}
}
} // 将排好队的顾客加入收银台顾客队列
cashDesk.customersComing(customers);
// 顾客来了,唤醒当前休息的收银台处理线程
this.notify();
} @Override
public void run() {
// 收银台处理永不停止
for(;;){
// 没有顾客,等待
if(cashDesk.queueIsEmpty()){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 被唤醒后,处理队伍头的顾客买单
cashDesk.processFirstCustomerCash();
}
}
// 获取收银台当前排队数
public int getCashDeskQueueSize(){
return cashDesk.getQueueSize();
}
// 获取收银台最后一个人货物数
public int getLastCustmGoodsNum(){
return cashDesk.getNumOfGoods();
}
}此外,还需要一个辅助的工具类,实现一组顾客到底加入到哪个收银员队列,实现如下:/**
* 提供辅助方法,帮助实现将,一组顾客根据当前收银台排队情况,选择不同的收银台
*/
public class DeskChoserHelper { /**
* 参照条件2 新来的一组顾客根据当前收银台排队情况,选择不同的收银台
*/
public static void choseCashDesk(List<Customer> customers, List<CashProcessor> cashProcessors) {
if (null == customers || 0 == customers.size()) {
System.out.println("没有顾客前来结账...");
return;
}
if (null == cashProcessors || 0 == cashProcessors.size()) {
System.out.println("没有可用的收银处理器...");
return;
} // 若收银台排队处理器只有一个,则直接调用
if (1 == cashProcessors.size()) {
cashProcessors.get(0)
.customersComing(customers.toArray(new Customer[customers.size()]));
return;
} // 按照AB两类顾客的喜好,获得符合要求的两个收银台
CashProcessor cashProcessorA = getCashProcessorA(cashProcessors);
CashProcessor cashProcessorB = getCashProcessorB(cashProcessors); // 若AB两个最优选择一样,则顾客无需分组
if (cashProcessorA == cashProcessorB) {
cashProcessorA.customersComing(customers.toArray(new Customer[customers.size()]));
return;
} // 将顾客分成A/B两类
List<Customer> customerA = new ArrayList<Customer>();
List<Customer> customerB = new ArrayList<Customer>();
for (Customer tmp : customers) {
if (tmp.getType() == CustomType.A) {
customerA.add(tmp);
} else {
customerB.add(tmp);
}
} // 分别放到AB两个收银台队列处理器中
cashProcessorA.customersComing(customerA.toArray(new Customer[customerA.size()]));
cashProcessorB.customersComing(customerB.toArray(new Customer[customerB.size()]));
} /**
* B顾客排队总是选择 队末尾的那个人商品个数越少的
*/
private static CashProcessor getCashProcessorB(List<CashProcessor> cashProcessors) {
CashProcessor cashProcessorB = cashProcessors.get(0);
for (int i = 1; i < cashProcessors.size(); i++) {
if (cashProcessors.get(i).getLastCustmGoodsNum() < cashProcessorB
.getLastCustmGoodsNum()) {
cashProcessorB = cashProcessors.get(i);
}
}
return cashProcessorB;
} /**
* A顾客排队总是选择 人数最少的队
*/
private static CashProcessor getCashProcessorA(List<CashProcessor> cashProcessors) {
CashProcessor cashProcessorA = cashProcessors.get(0);
for (int i = 1; i < cashProcessors.size(); i++) {
if (cashProcessors.get(i).getCashDeskQueueSize() < cashProcessorA
.getCashDeskQueueSize()) {
cashProcessorA = cashProcessors.get(i);
}
}
return cashProcessorA;
}}对于时间的操作,应该可以再来一个抽象,这里就没再做了。
关于条件1编号的问题,个人感觉应该属于人为设置的,就没再细分了。另外,这面试题,感觉有点大了,一时半会应该搞不定的。或者我想复杂了。
真的十分、万分的感谢版主用代码详细的解答。太感谢了。
我有几个问题
1. 版主加了一个DeskChoserHelper 类,里面是关于A,和B类型客户如何选择 收银台的,这两个方法能不能写到customer 的类中。
2. 能否将customer抽象成一个super class, 然后type A 和type B分别是他的一个子类。overridden的方法就是选择 收银台的方式不同。
3. 最后的main函数中,如何实例这N个cashprocessor, 使其多线程同步,是用for循环将其一个个instance吗,然后放在数组中。 如果是一个个的实现,那customer选择的时候是不是要访问每一个cashprocessor的状态啊?而且这个cashprocessor的状态得在每一分钟都有一个输出,因为几乎每一分钟都有顾客去进行结账,结账前就要去选择这个cashprocessor的队列。 这一点是我一直搞不清楚的,不知道该怎么实现。还有一个请求,既然版主就差一个time没有写了,恳请版主帮我实现了,好嘛,拜托了。
感激不尽!!!
2. type A、B是枚举,区分用的,也可以用int型,没必要做成继承关系。当然,我是图省事儿就写成内部类了,完全可以提取出来单独定义(做成内部类,也是为了说明,这个枚举是给外部类用的)
3. cashprocessor是实现Runnable的,可以使用new Thread(cashprocessor).start()的方式启动。另外,main方法可以看做最核心的“中央处理器”,它负责定义顾客、收银台实体,并且负责使用DeskChoserHelper实现顾客加入process的事件触发最后呢,那个time,可以定义一个静态的public static int time=0;然后每次事件触发,都实现time+1的操作,打印的时候打印time即可。嗯,代码也是纠结了一段时间才写好的,LZ最好自己改改测测,感觉重点就是两个:业务抽象和多线程使用。其它time什么的都是浮云。
我的想法的是用 到达时间,离开时间,等待时间去处理,不知道这样的想法可以实现吗?
谢谢啦~
2 (收银台的数量)
A 1 5
B 2 1
A 3 5
B 5 3
A 8 2所以这个顾客数是一定的,收银台数是一定的,只需要调用就能使用。
能不能根据顾客的属性,去选择哪个收银台,比如 到达时间,处理商品时间,完成时间,这几个变量去判断哪个收银台,以及完成的总时间
就可以不用线程去实现。大家觉得怎么样呢,给点建议吧。谢啦~