1.功能要求

  实验室有固定台数的设备供学生通过网络连接进行实验,一台设备只能同时被一个用户使用,一个用户只能占用一台设备。

  下面是一个功能的简图:

  

2.实现方案

  2.1 初始化

  

    在项目启动之后,开始进行实验设备排队功能的初始化,需要初始化的有:

      a,新建用于存放设备的队列,并从数据库中查出所有可正常使用的设备放入队列中;

      b,新建一个用于排队的线程池,后面会说明用途;

      c,新建一个用于存放排队用户的队列。

  2.2 流程实现

    Thread :当前的用户的请求线程;waitUsers:存放排队用户线程的队列;Exec:排队的线程池;threadA:在线程池中开启的排队线程;Equipment:存放设备的线程

3.具体实现

  3.1.队列初始化

 /**
* 初始化队列及线程池
* @author yangc
*
*/
public class EquipmentQueue {
//设备队列
public static BlockingQueue<Equipment> equipment;
//请求队列
public static BlockingQueue<WaitUser> waitUsers;
//线程池
public static ExecutorService exec; /**
* 初始化设备、请求队列及线程池
*/
public void initEquipmentAndUsersQueue(){
exec = Executors.newCachedThreadPool();
equipment=new LinkedBlockingQueue<Equipment>();
//将空闲的设备放入设备队列中
setFreeDevices(exec);
waitUsers=new LinkedBlockingQueue<WaitUser>();
} /**
* 将空闲的设备放入设备队列中
* @param exec
*/
private void setFreeDevices(ExecutorService exec) {
//获取可用的设备
List<Record> equipments=getFreeEquipment();
for (int i = 0; i < equipments.size(); i++) {
Record dc=equipments.get(i);
Equipment de=new Equipment(dc.getInt("id"),dc.getStr("quip_no"),dc.getStr("name"));
try {
equipment.put(de);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} /**
* 获取可用的设备(从数据库中查出可用的设备)
* @return
*/
public List<Record> getFreeEquipment(){
return QuipPartsManager.manager.getFreeEquipment();
}
}

  3.2.过滤实验请求

    当用户的实验请求进入时,首先要判断用户在数据库中是否处于未退出的情况,如果处于未退出的状态,将状态改为已结束,然后重新进行排队。

    每次请求实验时,会在数据库中保存一条排队数据,记录排队的用户、状态、使用时间等等信息。WaitUser是实验请求对应的类,主要字段有:Thread(存放实验请求的线程对象)、Session(实验请求对应的用户session)、Test(数据库中排队数据对应的类)。当请求进入后会在过滤器中将用户的请求对象放入到用请求队列中。

 //判断当前的用户是否有未退出的实验并进行处理
TestManager.manager.setUserTestInfomation(session);
//获取当前的线程
Thread thread=Thread.currentThread();
//将当前用户为等待
Test test=TestManager.manager.SetUserTestStateForWait(session);
//创建当前的用户请求对象
WaitUser waitUser=new WaitUser();
waitUser.setThread(thread);
waitUser.setSession(session);
waitUser.setTest(test);
//将当前用户请求对象放入队列中
EquipmentQueue.waitUsers.add(waitUser);

  3.3.执行排队线程并挂起当前线程

    在线程池中分配一个线程给当前的请求,并运行此线程,然后将请求线程挂起。

 //在线程池中给当前的用户请求分配线程,运行等待分配设备
EquipmentQueue.exec.execute(waitUser);
//暂停当前的用户请求,当whetherWait等于2时,说明设备绑定已经完成,无需将当前线程挂起
synchronized(thread){
if(waitUser.getWhetherWait()!=2){
thread.wait();
}
}

    开始排队即运行WaitUser中的experiment方法,先从设备队列中获取一个设备,如果没有设备,当前线程将会进入堵塞状态,直到队列中放入设备;如果有设备,从请求队列中取出一个请求对象,设置请求与设备绑定。

 public class WaitUser implements Runnable{
//当前请求的线程对象
private Thread thread;
//当前用户的session对象
private HttpSession session;
//用于判断线程是否进入wait状态
private int whetherWait=0;
//用户的实验对象
private Test test; @Override
public void run() {
//当线程未中断时
while(!Thread.interrupted()){
experiment();
}
} /**
* 将实验信息存入数据库,用户信息从session获取,将使用的设备从队列中删除,将设备对象存入session
*/
public void experiment(){
try {
//取出一个设备
Equipment equipment=EquipmentQueue.equipment.take();
EquipmentQueue.equipment.remove(equipment);
WaitUser waitUser=EquipmentQueue.waitUsers.take();
EquipmentQueue.waitUsers.remove(waitUser);
//将设备与用户绑定,状态设置为试验中
TestManager.manager.bindUserAndEquipment(equipment,waitUser);
} catch (InterruptedException e) {
System.err.println("---" + e.getMessage());
}
} public Test getTest() {
return test;
} public void setTest(Test test) {
this.test = test;
} public int getWhetherWait() {
return whetherWait;
} public void setWhetherWait(int whetherWait) {
this.whetherWait = whetherWait;
} public HttpSession getSession() {
return session;
} public void setSession(HttpSession session) {
this.session = session;
} public Thread getThread() {
return thread;
} public void setThread(Thread thread) {
this.thread = thread;
} }

WaitUser

  3.4.释放请求线程

    当设备绑定成功后,即可释放请求线程,这里有一个需要注意的问题,挂起请求线程与释放请求线程的先后关系(确保不会出现先释放后挂起的情况),把释放与挂起线程放到用显式锁修饰的代码块中,确保同时只会执行一处。当释放锁之后将状态WhetherWait的值设为2,标记此请求已经与设备绑定,不需要挂起。

 Thread thread=waitUser.getThread();
synchronized (thread) {
waitUser.setWhetherWait(2);
thread.notify();
}

4.补充

  4.1.排队时,给予客户端的反馈

    每次请求实验会存入一条排队数据到数据库中,当完成设备绑定后会将排队数据的状态值设置为“正在使用”,可以在客户端执行一个定时任务,定时从数据库查询处于“排队中”的请求数量,这样就可以在客户端实时显示“排队中,您前面还有10位用户正在等待...”的效果。

  4.2.用户进入实验后,长时间暂用而不使用的情况处理

    当用户进入实验后,为防止用户长时间的占用实验设备(并没有在使用),需要执行一个定时任务,每间隔一段时间,弹窗确认用户是否在使用,如果用户没有回应,则视为用户已经离开,将用户与设备解绑并退出实验。

  4.3.用户强行退出的处理(关闭页面/管理浏览器/关闭电脑...强行退出实验的情况)

    当用户强行退出时,在数据库中用户与设备依然处于绑定状态。当用户进入实验后,需要在客户端执行一个定时任务,实时的更新用户的最新实验时间。然后在服务端运行一个定时任务,实时检查数据库中的排队数据,判断状态为“正在使用”的数据中是否有最新的实验时间与当前时间的差值是否大于客户端定时任务的间隔时间(考虑到时间的更新会有一定的延迟,可以适当留些余量)的数据,如果大于则设备与用户解绑。

实验排队功能实现(JAVA)的更多相关文章

  1. 20145213《Java程序设计》实验报告一:Java开发环境的熟悉(Windows+IDEA)

    20145213<Java程序设计>实验报告一:Java开发环境的熟悉(Windows+IDEA) 实验要求 使用JDK编译.运行简单的Java程序. 使用IDEA编辑.编译.运行.调试J ...

  2. 20145206邹京儒《Java程序设计》实验报告一:Java开发环境的熟悉(Windows+IDEA)

    20145206<Java程序设计>实验报告一:Java开发环境的熟悉(Windows+IDEA) 实验内容及步骤 1.使用JDK编译.运行简单的Java程序: 建立实验目录: 在IDEA ...

  3. 20145233韩昊辰 《Java程序设计》实验报告一:Java开发环境的熟悉(Windows+IDEA)

    20145233 <Java程序设计>实验报告一:Java开发环境的熟悉 实验要求 使用JDK编译.运行简单的Java程序: 使用IDEA 编辑.编译.运行.调试Java程序. 实验内容 ...

  4. 20145221 《Java程序设计》实验报告二:Java面向对象程序设计

    20145221 <Java程序设计>实验报告二:Java面向对象程序设计 实验要求 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O. ...

  5. Java实验报告二:Java面向对象程序设计

    Java实验报告二:Java面向对象程序设计                                                                               ...

  6. 20145221 《Java程序设计》实验报告一:Java开发环境的熟悉(Windows+IDEA)

    20145221 <Java程序设计>实验报告一:Java开发环境的熟悉(Windows+IDEA) 实验要求 使用JDK编译.运行简单的Java程序: 使用IDEA 编辑.编译.运行.调 ...

  7. 20145205 《Java程序设计》实验报告五:Java网络编程及安全

    20145205 <Java程序设计>实验报告五:Java网络编程及安全 实验要求 1.掌握Socket程序的编写: 2.掌握密码技术的使用: 3.客户端中输入明文,利用DES算法加密,D ...

  8. 20145212《Java程序设计》实验报告一:Java开发环境的熟悉(Windows+IDE)

    20145212<Java程序设计>实验报告一:Java开发环境的熟悉(Windows+IDE) 实验内容及步骤 1.命令行下的JAVA程序开发 建立并进入实验目录: 撰写简单的Hello ...

  9. Java实验报告五:Java网络编程及安全

    Java实验报告五:Java网络编程及安全                                                                               ...

随机推荐

  1. PHP多进程编程pcntl_fork解

    其实PHP是支持并发的,只是平时很少使用而已.平时使用最多的应该是使用PHP-FMP调度php进程了吧. 但是,PHP的使用并不局限于做Web,我们完全也可以使用PHP来进行系统工具类的编程,做监控或 ...

  2. MyEclipse/Eclipse 使用图文详解

    引言 某天在群里看到有小伙伴问MyEclipse/Eclipse的一些使用问题,虽然在我看来,问的问题很简单,但是如果对于刚刚学习的人来说,可能使用就不那么友好了.毕竟我在开始使用MyEclipse/ ...

  3. 微信公众号开发——关于“WeixinJSBridge.call('closeWindow');”无效的问题

    最近在做微信公众号的开发,再做一个jsp的用户绑定页面,设置了一个timestamp,想实现的是当链接超时时alert一个窗口提示然后关闭网页窗口 但是呢,在jsp页面内直接 out.print(&q ...

  4. KMP算法的来龙去脉

    1. 引言 字符串匹配是极为常见的一种模式匹配.简单地说,就是判断主串TT中是否出现该模式串PP,即PP为TT的子串.特别地,定义主串为T[0-n−1]T[0-n−1],模式串为P[0-p−1]P[0 ...

  5. 快速搭建属于自己的数据库——mongodb

    为了真实模拟一个项目上线,拥有前端后端数据库都具备的功能,我选择了mongodb作为项目的数据库支持,这里分享一些mongodb的经验心得和血的教训. mongoddb安装 在本地安装 直接通过官网下 ...

  6. ArrayList ConcurrentModificationException

    1.ConcurrentModificationException ConcurrentModificationException 出现在使用 ForEach遍历,迭代器遍历的同时,进行删除,增加出现 ...

  7. 关于 LindedList 我想说

    LinkedList 的一些认识: 继承于AbstractSequentialList的双向链表,可以被当作堆栈.队列或双端队列进行操作 有序,非线程安全的双向链表,默认使用尾部插入法 适用于频繁新增 ...

  8. The Twin Towers zoj2059 DP

    The Twin Towers Time Limit: 2 Seconds      Memory Limit: 65536 KB Twin towers we see you standing ta ...

  9. 怎样学好哲学(lucas+费马小定理)

    怎样学习哲学 时间限制: 1 Sec  内存限制: 128 MB提交: 97  解决: 27[提交][状态][讨论版] 题目描述 OI大师抖儿在夺得银牌之后,顺利保送pku.这一天,抖儿问长者:&qu ...

  10. oracle排序的几种方法

    1.创建数据库表 CREATE TABLE USER_INFO(  USERID      VARCHAR2(10 BYTE)                 NOT NULL,  USERNAME  ...