文章目录
- 队列和线程
- 定义BlockingQueue阻塞队列
- 初始化队列
- 定时任务处理队列
- 消费队列的线程
- ApplicationContext的实现
- 生产一个队列和消费一个队列
队列和线程
队列能够解决很多需求,如消息推送,发布,订阅,根据需求完成业务逻辑处理高并发请求。
JDK 自带FIFO(FIFO First Input First Output先进先出)队列的选择。如何正确选择队列呢?
最重要的一点是要保证线程安全,使用BlockingQueue阻塞队列,它就是一个阻塞队列,那何为阻塞队列?
简单来说,就是在BlockingQueue为空时从队头取数据将会被阻塞,因为此时还没有数据可取,一旦队列中有数据了,取数据的线程就会释放得到了数据;如果BlockingQueue有容量限制且满了,那么插入数据的线程将会阻塞,知道队列中有空闲位置可以插入数据了,才会释放。经过上面一段描述,可以发现这不就是一个生产者-消费者模型吗?
这样保证了数据的有序,线程的安全。
实现生产者与消费者模式图解:
接下来就是BlockingQueue的实际运用。
定义BlockingQueue阻塞队列
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.context.ServletContextAware;
import javax.servlet.ServletContext;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @Author: volc
* @Description: 定义队列
* @Date: 22:27 2018/3/17
*/
public class InitFIFOListener implements InitializingBean, ServletContextAware {
public BlockingQueue<Object> queue = new LinkedBlockingQueue<Object>(); // 可设定大小
public void setServletContext(ServletContext context) {
// TODO Auto-generated method stub
}
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
}
public BlockingQueue<Object> getQueue() {
return queue;
}
public void setQueue(BlockingQueue<Object> queue) {
this.queue = queue;
}
}
初始化队列
<!--初始化全局(FIFO队列) -->
<bean id="initFIFO" class="com.biusoft.core.modules.zhxc.task.InitFIFOListener">
</bean>
定时任务处理队列
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.baomidou.springwind.common.ResponseInfo;
import com.baomidou.springwind.service.service.IWeatherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @Author: volc
* @Description: 定时任务消费和处理队列
* @Date: 11:47 2018/1/12
*/
@Component
public class AnalysisTask {
private static Log logger = LogFactory.getLog(AnalysisTask.class);
@Autowired
private InitFIFOListener listener;
// 0 0/60 * * * ? 每个一小时执行 "0/10 * * * * ?" 间隔10秒执行
@Scheduled(cron = "0 0/60 * * * ?") // 间隔5秒执行
public void analysis() throws IOException {
logger.debug("?&=======开始消费队列任务:");
// 创建线程池大小
ExecutorService exec = Executors.newFixedThreadPool(1);
if (null != listener.queue) {
BlockingQueue<Object> queue = listener.queue;
if (queue.size() > 0) {
logger.debug("?&=======队列大小:" + queue.size());
logger.debug("?&=======正从队列获取数据..." + queue.size());
Map<String, Object> poll = (Map<String, Object>) queue.poll();
logger.debug("?&=======正在消费数据:" + poll.toString());
exec.execute(new AnalysisGpsThread((String) poll.get("url"), (String) poll.get("id")));
}
}
/**
* ExecutorService并不会立即关闭,但是ExecutorService已经不会再接收新的task了,等到所有的thread完成各自的task,那么这个ExecutorService就会关闭
*/
exec.shutdown();
logger.debug("?&=======结束消费队列任务:");
// 本次任务中的全部线程执行完毕后再启动下一轮任务,当本次任务未结束时,将会进入这个死循环,防止下一轮任务进入
while (!exec.isTerminated()) {
}
}
}
消费队列的线程
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.biusoft.core.common.utils.SpringContextHolder;
import com.biusoft.core.modules.zhxc.service.AnalysisGpsService;
/**
* @Author: volc
* @Description: 异步解析gps文件的线程
* @Date: 21:52 2018/3/17
*/
public class AnalysisGpsThread implements Runnable {
// 线程停止标记
public volatile boolean exit = false;
private static Log logger = LogFactory.getLog(AnalysisGpsThread.class);
private static String url = "";
private static String id = "";
public AnalysisGpsThread(String url, String id) {
this.url = url;
this.id = id;
}
@Override
public void run() {
// S 业务处理
AnalysisGpsService analysisGpsService = SpringContextHolder.getBean("analysisGpsService"); // AnalysisGpsService已经通过@Service类注解注入容器,bean名称默认为"analysisGpsService"
logger.debug("?&=======启动解析线程");
try {
boolean result = analysisGpsService.gps(id, url);
logger.debug("?&=======线程执行结果" + result);
exit = true;
// E 业务处理
} catch (Exception e) {
e.printStackTrace();
}
while (!exit) {
}
}
}
ApplicationContext的实现
SpringContextHolder这个类就可以方便获得ApplicationContext中的所有bean
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import com.biusoft.core.common.config.Global;
/**
* 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候取出ApplicaitonContext.
*/
@Service // 注解注入或配置注入spring容器
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
if (logger.isDebugEnabled()){
logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
}
applicationContext = null;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
@Override
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
}
}
生产一个队列和消费一个队列
接口中创建一条数据到队列中
// 获取已经初始化的InitFIFOListener对象
@Autowired
private InitFIFOListener listener;
// 定义队列容器
private BlockingQueue<Object> queue = null;
// 项目全局FIFO 队列
if (null == queue) {
queue = listener.queue;
}
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("url", jsonNode.get("msg").get("url").asText());
paramMap.put("id", xunCJL.getId());
queue.offer(paramMap);
logger.debug("队列大小:" + queue.size());
定时任务中则查询是否有队列存在如果有则开始消费队列。