文章目录

  • 队列和线程
  • 定义BlockingQueue阻塞队列
  • 初始化队列
  • 定时任务处理队列
  • 消费队列的线程
  • ApplicationContext的实现
  • 生产一个队列和消费一个队列


队列和线程

队列能够解决很多需求,如消息推送,发布,订阅,根据需求完成业务逻辑处理高并发请求。
JDK 自带FIFO(FIFO First Input First Output先进先出)队列的选择。如何正确选择队列呢?
最重要的一点是要保证线程安全,使用BlockingQueue阻塞队列,它就是一个阻塞队列,那何为阻塞队列?
简单来说,就是在BlockingQueue为空时从队头取数据将会被阻塞,因为此时还没有数据可取,一旦队列中有数据了,取数据的线程就会释放得到了数据;如果BlockingQueue有容量限制且满了,那么插入数据的线程将会阻塞,知道队列中有空闲位置可以插入数据了,才会释放。经过上面一段描述,可以发现这不就是一个生产者-消费者模型吗?
这样保证了数据的有序,线程的安全。

实现生产者与消费者模式图解:

java 队列线程安全么_生产者-消费者模型


接下来就是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());

定时任务中则查询是否有队列存在如果有则开始消费队列。