阻塞队列:import java.util.concurrent.BlockingQueue;
线程池:import java.util.concurrent.ExecutorService;
初始化队列和线程池:
package com.gxhc.alarm.listener;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import com.gxhc.alarm.service.AlarmService;
import com.gxhc.alarm.listener.QueueWatcher;
import com.gxhc.util.ConstantUtil;
import com.gxhc.util.ContextUtil;
public class QueueListener implements ServletContextListener {
/**
* 初始化加载
*/
@Override
public void contextInitialized(ServletContextEvent arg0) {
setQueue();
setWatcher();
setFixedThreadPool();
}
//初始化队列
public void setQueue(){
ConstantUtil.alarmQueue = new LinkedBlockingQueue<Object>(10000);
}
//初始化被观察者
public void setWatcher(){
//modify by sl 原来的被观察者,不是上下文中的alarmService ,无法触发观察者事件
//ConstantUtil.alarmService = new AlarmService();
ConstantUtil.alarmService = ContextUtil.getBean("alarmService",AlarmService.class);
QueueWatcher queueWatcher = new QueueWatcher(ConstantUtil.alarmService);
}
//初始化线程池
public void setFixedThreadPool(){
// 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
ConstantUtil.alarmFixedThreadPool = Executors.newFixedThreadPool(10);
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
}
}
第一次,触发观察者,启动队列线程,当阻塞队列中无数据时,该线程会进入阻塞状态,直到队列中有数据,才会变成运行状态;
package com.gxhc.alarm.listener;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import com.gxhc.alarm.service.AlarmService;
import com.gxhc.alarm.table.entity.AlarmLog;
import com.gxhc.util.ConstantUtil;
import com.gxhc.util.ContextUtil;
public class QueueWatcher implements Observer{
public QueueWatcher(Observable o){
o.addObserver(this);
}
public QueueWatcher(){}
/* (非 Javadoc)
*
* 观察者只被触发一次,后续消息的推送由队列维护
* @param o 被观察者
* @param arg
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
@Override
public void update(Observable o, Object arg) {
//通过定长java线程池执行该操作,防止无限启动线程导致程序卡死。
ConstantUtil.alarmFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
// TODO Auto-generated method stub
ConstantUtil.IsGeginQueue = true;
AlarmService alarmService = ContextUtil.getBean("alarmService",AlarmService.class);
while (ConstantUtil.IsGeginQueue) {
//如果阻塞队列中无元素可取,则线程进入阻塞状态,直到该队列中再次拥有元素时,激活。
//AlarmLog alarmLog = (AlarmLog)ConstantUtil.alarmQueue.take();
Map<String, Object> map = (Map<String, Object>)ConstantUtil.alarmQueue.take();
alarmService.insertMessage((AlarmLog)map.get("alarmLog"),String.valueOf(map.get("reciveUser")),String.valueOf(map.get("reciverDept")),String.valueOf(map.get("cid")));
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
/*ConstantUtil.alarmFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
AlarmLog alarmLog = (AlarmLog)ConstantUtil.alarmQueue.take();
AlarmService alarmService = ContextUtil.getBean("alarmService",AlarmService.class);
// TODO Auto-generated method stub
alarmService.setMessage(alarmLog,null,null);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});*/
// 触发订阅处理
//alarmService.setMessage(((AlarmService)o).getAlarmLog(),null,null);
}
}
所以,当启动队列线程后,不用再次启动,后续消息的推送,只需要往队列里放数据就行了。
/*******************************************关于阻塞队列的拓展***************************************************************/
1.阻塞队列
在阻塞队列中有生产者和消费者的概念,生产者往队列中放数据,而消费者负责消费(即使用和从队列中移除)产品。
当队列中元素已经满的时候,生产产品的线程会被阻塞,当队列中无元素时,消费产品的线程会被阻塞。
当再次满足上述条件时,对应的线程又会被再次唤醒。
1.1 JDK提供的阻塞队列:
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列,遵循FIFO原则。
LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列,遵循FIFO原则,默认和最大长度Integer.MAX_VALUE。
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
1.2 BlockingQueue中API介绍
offer(E e): 将给定的元素设置到队列中,如果设置成功返回true, 否则返回false. e的值不能为空,否则抛出空指针异常。
offer(E e, long timeout, TimeUnit unit): 将给定元素在给定的时间内设置到队列中,如果设置成功返回true, 否则返回false.
add(E e): 将给定元素设置到队列中,如果设置成功返回true, 否则抛出异常。如果是往限定了长度的队列中设置值,推荐使用offer()方法。
put(E e): 将元素设置到队列中,如果队列中没有多余的空间,该方法会一直阻塞,直到队列中有多余的空间。
take(): 从队列中获取值,如果队列中没有值,线程会一直阻塞,直到队列中有值,并且该方法取得了该值。
poll(long timeout, TimeUnit unit): 在给定的时间里,从队列中获取值,如果没有取到会抛出异常。
remainingCapacity():获取队列中剩余的空间。
remove(Object o): 从队列中移除指定的值。
contains(Object o): 判断队列中是否拥有该值。
drainTo(Collection c): 将队列中值,全部移除,并发设置到给定的集合中
/****************************************关于线程池的拓展***********************************************/
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
ExecutorService alarmFixedThreadPool = Executors.newFixedThreadPool(10);