本人面试遇到的多线程笔试题,主要考察的多线程编程和面向对象思想。主要关注:多个窗口办理业务,有VIP窗口和普通窗口。VIP窗口可以办理普通业务,普通业务窗口只能办理普通业务,VIP用户在VIP窗口可以优先办理。一般核心业务实现出来就可以了。
题目
1、银行内有6个业务窗口,1-4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口;
2、有三种对应类型的客户:VIP客户,普通客户,快速客户
3、异步随机生成各种类型客户,生成各类型用户的概率比例为:VIP客户:快速客户:普通客户 = 1:3:6
4、客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值
5、各类型客户在其对应窗口按顺序依次办理业务;
6、VIP窗口和快速窗口优先处理对应客户的业务,这两个窗口也可以处理普通客户的业务。
分析
- 对象:窗口、银行顾客、取号机
- 窗口类型:普通窗口、VIP窗口、快速窗口。
- 业务类型:普通业务、VIP业务、快速业务。
- 多线程:每一个窗口为一个线程,顾客生成一个线程。
代码
顾客实体
public class Consumer {
//取号号码
public Integer id;
//类型:普通、快速、VIP
public ConsumerType consumerType;
public Consumer(Integer id, ConsumerType consumerType) {
this.id = id;
this.consumerType = consumerType;
}
//省略set、get
/**
* 模拟业务执行时间
*/
public void execute() {
Random random = new Random();
try {
switch (consumerType) {
case NORMAL:
TimeUnit.SECONDS.sleep(random.nextInt(10) + 1);
break;
case QUICK:
TimeUnit.SECONDS.sleep(1);
break;
case VIP:
TimeUnit.SECONDS.sleep(random.nextInt(10) + 1);
break;
}
} catch (Exception exception) {
exception.printStackTrace();
System.out.println("业务异常");
}
}
}
//顾客枚举类
public enum ConsumerType {
NORMAL, QUICK, VIP;
@Override
public String toString() {
String name = null;
switch (this) {
case NORMAL:
name = "普通";
break;
case QUICK:
name = "快速";
break;
case VIP:
name = name();
break;
}
return name;
}
}
取号机(用户和号码管理器)
使用线程安全的阻塞队列BlockingQueue和原子类AtomicInteger实现用户取号排队。
public class ConsumerManager {
/**
* 用户号码(原子类线程安全)
*/
public AtomicInteger norNumber = new AtomicInteger(1);
public AtomicInteger vipNumber = new AtomicInteger(1);
public AtomicInteger quickNumber = new AtomicInteger(1);
/**
* 用户队列(线程安全)
*/
public ArrayBlockingQueue<Consumer> norQueue = new ArrayBlockingQueue<>(10);
public ArrayBlockingQueue<Consumer> vipQueue = new ArrayBlockingQueue<>(10);
public ArrayBlockingQueue<Consumer> quickQueue = new ArrayBlockingQueue<>(10);
/**
* 按照概率(VIP:快速:普通=1:3:6)产生用户
*/
public void produce() {
Random random = new Random();
try {
while (true) {
// put阻塞添加客户
int type = random.nextInt(10);
if (type < 1) {
System.out.println("产生第" + vipNumber.get() + "号vip顾客");
vipQueue.put(new Consumer(vipNumber.getAndIncrement(), ConsumerType.VIP));
} else if (type < 4) {
System.out.println("产生第" + quickNumber.get() + "号快速顾客");
quickQueue.put(new Consumer(quickNumber.getAndIncrement(), ConsumerType.QUICK));
} else {
System.out.println("产生第" + norNumber.get() + "号普通顾客");
norQueue.put(new Consumer(norNumber.getAndIncrement(), ConsumerType.NORMAL));
}
// 根据情况停顿一秒,方便观察
TimeUnit.SECONDS.sleep(1);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public ArrayBlockingQueue<Consumer> getNorQueue() {
return norQueue;
}
public void setNorQueue(ArrayBlockingQueue<Consumer> norQueue) {
this.norQueue = norQueue;
}
public ArrayBlockingQueue<Consumer> getVipQueue() {
return vipQueue;
}
public void setVipQueue(ArrayBlockingQueue<Consumer> vipQueue) {
this.vipQueue = vipQueue;
}
public ArrayBlockingQueue<Consumer> getQuickQueue() {
return quickQueue;
}
public void setQuickQueue(ArrayBlockingQueue<Consumer> quickQueue) {
this.quickQueue = quickQueue;
}
}
窗口类
分别定义三个不同的窗口,抽象公共特性。每个窗口都持有ConsumerManager (取号机)的实例,获取对应顾客办理业务。
public abstract class Window {
/**
* 窗口号
*/
public Integer windowId;
/**
* 窗口类型
*/
public String windowType;
public Window(Integer windowId, String windowType) {
this.windowId = windowId;
this.windowType = windowType;
}
/**
* 公共服务方法
*/
public abstract void service(ConsumerManager consumerManager);
}
/**
* 普通窗口
*/
public class NormalWindow extends Window {
public NormalWindow(int i) {
super(i, "普通");
}
@Override
public void service(ConsumerManager consumerManager) {
while (true) {
try {
ArrayBlockingQueue<Consumer> norQueue = consumerManager.getNorQueue();
// TODO 普通窗口一直阻塞获取用户
System.out.println(windowId + "号" + windowType + "窗口空闲中~~~~");
Consumer consumer = norQueue.take();
System.out.println(windowId + "号" + windowType + "窗口业开始办理第" + consumer.getId() + "号" + consumer.getConsumerType().toString() + "顾客的业务");
consumer.execute();
System.out.println(windowId + "号" + windowType + "窗口办理完了第" + consumer.getId() + "号" + consumer.getConsumerType().toString() + "顾客的业务");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 快速窗口
*/
public class QuickWindow extends Window {
public QuickWindow(int i) {
super(i, "快速");
}
@Override
public void service(ConsumerManager consumerManager) {
while (true) { // 优先快速业务
ArrayBlockingQueue<Consumer> quiQueue = consumerManager.getQuickQueue();
Consumer consumer = quiQueue.poll();
if (consumer != null) {
System.out.println(windowId + "号" + windowType + "窗口开始办理第" + consumer.getId() + "号" + consumer.getConsumerType().toString() + "顾客的业务");
consumer.execute();
System.out.println(windowId + "号" + windowType + "窗口办理完了第" + consumer.getId() + "号" + consumer.getConsumerType().toString() + "顾客的业务");
} else { // 普通业务
ArrayBlockingQueue<Consumer> norQueue = consumerManager.getNorQueue();
consumer = norQueue.poll();
if (consumer != null) {
System.out.println(windowId + "号" + windowType + "窗口业开始办理第" + consumer.getId() + "号" + consumer.getConsumerType().toString() + "顾客的业务");
consumer.execute();
System.out.println(windowId + "号" + windowType + "窗口办理完了第" + consumer.getId() + "号" + consumer.getConsumerType().toString() + "顾客的业务");
}
}
}
}
}
/**
* VIP窗口
*/
public class VIPWindow extends Window {
public VIPWindow(int i) {
super(i, "VIP");
}
@Override
public void service(ConsumerManager consumerManager) {
while (true) { // 优先VIP业务
ArrayBlockingQueue<Consumer> vipQueue = consumerManager.getVipQueue();
Consumer consumer = vipQueue.poll();
if (consumer != null) {
System.out.println(windowId + "号" + windowType + "窗口开始办理第" + consumer.getId() + "号" + consumer.getConsumerType().toString() + "顾客的业务");
consumer.execute();
System.out.println(windowId + "号" + windowType + "窗口办理完了第" + consumer.getId() + "号" + consumer.getConsumerType().toString() + "顾客的业务");
} else { // 普通业务
ArrayBlockingQueue<Consumer> norQueue = consumerManager.getNorQueue();
consumer = norQueue.poll();
if (consumer != null) {
System.out.println(windowId + "号" + windowType + "窗口业开始办理第" + consumer.getId() + "号" + consumer.getConsumerType().toString() + "顾客的业务");
consumer.execute();
System.out.println(windowId + "号" + windowType + "窗口办理完了第" + consumer.getId() + "号" + consumer.getConsumerType().toString() + "顾客的业务");
}
}
}
}
}
测试
线程池启动线程,分别创建4个普通窗口、一个快速窗口、一个VIP窗口,并用不同线程启动。另外启动一个顾客生成线程。(推荐手动创建线程池,不要使用jdk自带的Executors工具类)直接运行main方法。
public class MainTest {
public static void main(String[] args) throws InterruptedException {
final ConsumerManager consumerManager = new ConsumerManager();
final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(7, 7, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
//final ExecutorService threadPool = Executors.newFixedThreadPool(7);
threadPool.execute(() -> {
// 持续按概率产生顾客
consumerManager.produce();
});
// 产生4个普通窗口
for (int i = 1; i < 5; i++) {
Window normalWindow = new NormalWindow(i);
threadPool.execute(() -> {
normalWindow.service(consumerManager);
});
}
// 5号快速窗口
Window quickWindow = new QuickWindow(5);
threadPool.execute(() -> {
quickWindow.service(consumerManager);
});
// 6号VIP窗口
Window vipWindow = new VIPWindow(6);
threadPool.execute(() -> {
vipWindow.service(consumerManager);
});
}
}