关于ArrayBlockingQueue队列的一些问题
来源:互联网 发布:顶尖微信数据恢复软件 编辑:程序博客网 时间:2024/05/16 12:41
背景:最近接手一个新的应用-商机快递,主要是给用户发送营销邮件,由于不定期会有漏发的情况,所以在里面加了一些逻辑来修复这个问题,由于系统采用了多线程的方式,改之前考虑的不周全,最后会导致重发的现象。
// 创建一个阻塞队列,容量为maxThread*2 ArrayBlockingQueue blockQueue = new ArrayBlockingQueue(maxThread * 2); exec = new BizExpressThreadPoolExecutor(maxThread, maxThread, 60, TimeUnit.SECONDS, blockQueue, maxThread * 2); exec.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 该方法可以给当前的进程注册一个清理线程,当进程退出的时候,会执行线程中的代码。 Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook())); while (!isShutDown) { exec.execute(new BizExpressMailRun()); try { Thread.sleep(2000); } catch (InterruptedException e) { } }
/** * @param corePoolSize 线程池维护线程的最少数量 * @param maximumPoolSize 线程池维护线程的最大数量 * @param keepAliveTime 线程池维护线程所允许的空闲时间 * @param unit 线程池维护线程所允许的空闲时间的单位 * @param workQueue 线程池所使用的缓冲队列 * @param semaphore */ public BizExpressThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, int semaphore){ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); available = new Semaphore(semaphore, true); }
系统初始时会实例一个ThreadPoolExecutor对象(exec)。ThreadPoolExecutor是并发包中一个提供线程池的服务,可以很容易将一个实现了Runnable接口的任务放入线程池中执行。具体的execute(Runnable)方法执行过程为:
首先判断传入的Runnable对象是否为null,如果为null直接抛出NullPointException异常。如果不为空执行下面步骤
如果当前的线程数小于配置的corePoolSize,则调用addIfUnderCorePoolSize方法进而会调用mainLock锁。
如果当前的线程数小于配置的corePoolSize并且线程处于RUNNING状态,调用addThread增加线程,
addThread方法首先创建Worker对象,然后调用threadFactory( Thread newThread(Runnable r); )创建新的线程,如果创建新的线程不为null时,将Worker对象的thread属性指向此创建出来的线程,并将此Worker对象放入到workers中,最后增加当前线程池中的线程数。
用代码描述为:
private List workers; private int count;//当前线程池中的线程数 public void addThread(Runnable r){ Worker worker=new Worker(); Thread tt=threadFactory.newThread(r); if(rr!=null){ worker.setThread(tt); } workers.add(worker); count++; }
--------------------------
while (!isShutDown) { exec.execute(new BizExpressMailRun()); try { Thread.sleep(2000); } catch (InterruptedException e) { } }
这是阻塞队列的执行入口,是一个循环过程,中间会休眠2秒,队列的长度是初始化时的corePoolSize,消费和生产无序进行。
原来的做方法是
public void doPerform() { List<BizExpressDailyDO> bizexpresses = bizExpressDailyDAO.fetchSomeBizExpressDaily( BizExpressConfig.getServerIp(), BizExpressConfig.getBuildEachFetchNum()); // 如果没有取到数据,则休眠5秒,避免反复读取数据库。 if (bizexpresses == null || bizexpresses.size() == 0) { try { Thread.currentThread().sleep(5000); // 补发发送失败邮件 doContinue(); } catch (InterruptedException e) { } return; } takeCareOf(bizexpresses); }
一个线程执行doPerform方法,会改biz_express_daily表中的记录的ip,然后再执行takeCareOf方法来发邮件,发送成功后会将记录的status字段值改掉,但是关键的是在完成status改掉之前会有一段时间。
另外的线程调用doPerform方法,此时bizexpresses 为空,进入到if分支,调用doContinue方法。
public void doContinue() { // 获取当日被选出,但未被处理过的纪录,最多取100条 List<BizExpressDailyDO> bizexpresses = bizExpressDailyDAO.fetchLastUnbuiltBizExpressDaily( BizExpressConfig.getServerIp(), BizExpressConfig.getBuildEachFetchNum()); if (bizexpresses.size() > 0) { takeCareOf(bizexpresses); } else { return; } try { Thread.sleep(5000); } catch (InterruptedException e) { } doContinue(); }
此时的bizexpresses 是有值的,而且取出来的正好是刚才第一个线程里面的值,然后同样执行takeCareOf方法发送邮件。
这样就会造成邮件的重发,当然根据线程抢占的激烈程度,会导致重发邮件的数量也不一致。
- 关于ArrayBlockingQueue队列的一些问题
- 关于栈与队列的一些问题
- 可阻塞的队列——ArrayBlockingQueue
- Java中的阻塞队列ArrayBlockingQueue的使用
- ArrayBlockingQueue阻塞队列的使用示例
- 关于jquery的ajax队列的一些问题
- 阻塞队列使用---ArrayBlockingQueue
- JAVA 阻塞队列 ArrayBlockingQueue
- 阻塞队列之ArrayBlockingQueue
- 多线程-队列ArrayBlockingQueue 、LinkedBlockingQueue
- 阻塞队列ArrayBlockingQueue说明
- 阻塞队列(ArrayBlockingQueue)
- 数组阻塞队列 ArrayBlockingQueue
- 阻塞队列之ArrayBlockingQueue
- 阻塞队列ArrayBlockingQueue
- 数组阻塞队列 ArrayBlockingQueue
- 多线程之间的交互:阻塞队列之ArrayBlockingQueue
- Java常用的2种阻塞队列ArrayBlockingQueue和LinkedBlockingQueue
- Object-C学习笔记九-----查找文件
- 2011-09-15仍旧学习jdbc
- 关于Flex+json+Java的整合开发【连载一】- 整体架构框架介绍
- jQuery学习资源
- ftruncate函数的使用
- 关于ArrayBlockingQueue队列的一些问题
- 面向对象的思想做图书卡片管理
- 常用排序算法——C实现
- Android应用程序四大组件之Service(一)
- lseek函数的用法
- BIEE10导出EXCEL避免自动变成科学计数法
- Js遍历Josn对象(内容对比页实现思路)
- 9.15
- webservice简介