澳门金莎娱乐网站四个有关定期生产与费用的难

时间:2019-10-04 14:48来源:编程技术
上篇 文章讲解了一个定时生产消费时候消费队列里面最多有几个元素的问题。本文来探讨另外一个问题,由于生产和消费线程执行的不确定性,会产生当生产线程t1时间投递任务到队列

上篇 文章讲解了一个定时生产消费时候消费队列里面最多有几个元素的问题。本文来探讨另外一个问题,由于生产和消费线程执行的不确定性,会产生当生产线程t1时间投递任务到队列后,消费线程可能在t1+1左右时候才会开始消费其中的一个队列,也就是生产与消费之间会有1s时间的的间隔,那么有没有办法保证生产线程t1时间投递完毕后,消费线程能在接近于t1时刻就开始消费那?

本文来讲解遇到的一个有意思的与定时器相关的生产消费模型,模型如下图:

澳门金莎娱乐网站 1image.png

澳门金莎娱乐网站 2image.png

t1时刻生产线程投递一个元素分别到队列1,2,假如t1+0.001时刻投递元素到第3个队列,而消费线程正好在t1+0.0001时刻刚刚处理完队列3发现队列为空后就放弃当前1s的执行,那么消费线程会在t1+0.0001+1时刻消费第一个队列的元素。到这里生成线程明明是t1时刻放入元素到队列,而消费线程却在1s后才开始处理。

  • 生产者是一个定时器线程,使用ScheduledThreadPoolExecutor的scheduleAtFixedRate控制每间隔3s投递一个元素到队列1,2,3(使用offer方法)。比如第1s放入一个元素到队列1,2,3,这时候每个队列里面有一个元素。然后第4s在放入一个元素到队列1,2,3,如果没有消费线程的时候这时候每个队列里面有2个元素.

  • 队列为有界阻塞队列(ArrayBlockingQueue),队列元素的大小设置为6个元素。

  • 消费者是一个定时器线程,使用ScheduledThreadPoolExecutor的scheduleAtFixedRate控制每间隔1s轮询取出每个队列里面全部元素。比如第1s取出队列1里面的全部元素,第2s取出队列2里面的元素,第3s取出队列3里面的元素,第4s取出队列1里面的元素....并且如果发现当前队列为空则会结束当前s的任务,然后等下1s到了的时候从下一个队列开始取。

  • 消费线程启动后,生产线程才启动,生产者和消费者线程优先级相同

  • 消费线程和生产线程里面没有耗时操作,或者耗时都不超过1s。这个保证定时器任务不会被延迟执行。

消费线程在执行当前1s的任务时候如果发现当前队列为空,则去看下一个队列,直到有一个队列不为空为止。这时候有可能在1s内队列都为空,那么定时消费线程的下一个定时任务会被延迟,不过没关系。但是这会导致消费线程同1s内可能消费了两个队列。那么有没有更好的方法那?

问题:每个队列里面最多时候会有几个元素?

既然要求生产后马上消费那自然会想到通知等待模型,也就是消费线程一开始阻塞,当生产线程把元素放入队列后,发送通知激活消费线程,那么直接用notify,wait?不不,并发包里面有个Semaphore可以做这个事情:

  • 假设第1s时候消费线程去获取第一个队列元素,这时候第一个队列为空,则当前任务结束,消费线程等到第2s时候会去第二个队列取元素。
  • 假设第1.000000000001s时候生产者线程放入元素到每个队列,这时候每个队列有一个元素
  • 第2s时候消费者线程去获取第二个队列元素,里面有一个元素,取出后,队列为空
  • 第3s时候消费者线程去获取第三个队列元素,里面有一个元素,取出后,队列为空
  • 第4s时候消费者线程去获取第一个队列元素,里面有一个元素,取出后,队列为空。
  • 第4.000000000001s时候生成者线程放入元素到每个队列,这时候每个队列元素为1个。
  • 生产线程放入元素到三个队列后,调用semaphore.release;释放3个信号量,内部信号量计数器值递增3;
  • 消费线程则使用如下结构:

按照上面的逻辑看的话,每个队列里面最多有一个元素。其实不然,因为在多线程模型中每个线程占用cpu执行的时间是按照时间片来划分的,每个线程执行完自己的时间片后会被挂起,然后下一个获取到时间片的线程会占用CPU执行自己的任务,当下一轮被挂起的线程获取到自己的时间片后,会恢复执行上下文从之前被挂起的地方执行。

所以这里步骤并不能保证比步骤先执行,有可能消费线程在执行步骤前时间片用完了,则这时候消费线程会被挂起,而如果现在生产者线程获取到了cpu并且到达了定时执行任务的时间点,则步骤会执行,那么这时候队列一,里面会有2个元素,那么等消费线程获取CPU时间片执行时候会从队列1里面拿到2个元素。

static volatile int curTagIndex = 0; public static void consume() { while (!Thread.currentThread().isInterrupted { try { //获取一个信号量,内部计数器会减轻1 semaphore.acquire(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } int seconds = new Date().getSeconds(); ArrayBlockingQueue<String> curTagQueue = tagQueue.get(curTagIndex); System.out.println(j.join("timer curqueue ", curTagIndex, curTagQueue.poll(), curTagQueue.size(), System.currentTimeMillis; //休眠1s try { Thread.sleep; } catch (InterruptedException e1) { Thread.currentThread().interrupt(); } curTagIndex++; if (curTagIndex >= TAG_QUEUE_SIZE) { curTagIndex = 0; } } }

注:这里使用1.000000000001s是为了说明和1s比较接近,其实由于影响调度因素很多,有可能有比这更接近1s的时间

定时生产消费模型一般使用的比较多,但是消费与生产直接可能会存在大概1s的延迟,一旦延迟产生,那么后面每个元素的消费都会进行延迟。而基于信号量的生成消费模型,使用通知等待,当元素生成后就通知消费者来消费,这在一定程度上减少了延迟。作者:加多

多线程下会遇到很多微妙的情况,有时候遇到的问题要结合OS的知识才能解释清楚,本节从其中一个角度分析了每个队列里面有可能会有两个元素的原因,欢迎大家补充其他原因,并考虑会不会存在一个队列里面最多时候有3个元素的情况

编辑:编程技术 本文来源:澳门金莎娱乐网站四个有关定期生产与费用的难

关键词:

  • 上一篇:没有了
  • 下一篇:没有了