Java多线程编程实战指南(核心篇)这段代码有问题吧?

最近在看线程中断的知识。不知道是不是我理解有问题还是代码有点问题。求大佬看看

第五章 5-18 通用的代码优雅停止办法的实例

public class TerminatableTaskRunner implements TaskRunnerSpec {
  protected final BlockingQueue<Runnable> channel;
  // 线程停止标记
  protected volatile boolean inUse = true;
  // 待处理任务计数器
  public final AtomicInteger reservations = new AtomicInteger(0);
  private volatile Thread workerThread;
  public TerminatableTaskRunner(BlockingQueue<Runnable> channel) {
    this.channel = channel;
    this.workerThread = new WorkerThread();
  }

  public TerminatableTaskRunner() {
    this(new LinkedBlockingQueue<Runnable>());
  }

  @Override
  public void init() {
    final Thread t = workerThread;
    if (null != t) {
      t.start();
    }
  }

  @Override
  public void submit(Runnable task) throws InterruptedException {
    channel.put(task);
    reservations.incrementAndGet();
  }

  public void shutdown() {
    Debug.info("Shutting down service...");
    inUse = false;// 语句①
    final Thread t = workerThread;
    if (null != t) {
      t.interrupt();// 语句②
    }
  }

  public void cancelTask() {
    Debug.info("Canceling in progress task...");
    workerThread.interrupt();
  }

  class WorkerThread extends Thread {
    @Override
    public void run() {
      Runnable task = null;
      try {
        for (;;) {
          // 线程不再被需要,且无待处理任务
          if (!inUse && reservations.get() <= 0) {// 语句③
            break;
          }
          task = channel.take();
          try {
            task.run();   //---语句五---真正的任务逻辑
          } catch (Throwable e) {
            e.printStackTrace();
          }
          // 使待处理任务数减少1
          reservations.decrementAndGet();// 语句④
        }// for循环结束
      } catch (InterruptedException e) {
        workerThread = null;
      }
      Debug.info("worker thread terminated.");
    }// run方法结束
  }// WorkerThread结束
}

这段代码讲的是让工作线程停止的。
生产者不断submit任务。消费者自旋消费阻塞队列里面的任务。
书上说中断标示位容易被task.run()(语句五)方法吞没掉(即其他线程调用workerThread.interrupt(),如果此时刚好在执行语句五,语句五抛Interrupt异常,中断标示位被清除,下一轮循环到take被不会被中断了。所以采用了inUse变量和reservations变量来解决中断标示位可能被清除的问题。

inUse变量是没问题,感觉加了reservations有点多余,甚至会导致在有种情况线程中断不了。

比如现在任务有3个(即reservations.get()=3) 。代码执行到语句五task.run()的时候,其他线程调用了shutdown()方法。语句五的run方法抛出InterruptException并清除了标记位置。这时候只能依赖inUse和reservations来break这个for循环让线程退出了。单单用inUse方法(即只判断!inUse就退出)本来是下次循环就能break了。但是如果加上reservations.get()<= 0 这个条件可能会出现一些问题。就是这个消费线程的消费速度(即执行语句五task.run()的速度)远不如其他生产线程的submit任务速度。这就会导致这个for循环会一直执行一下,导致线程永远无法退出。

我想到的方法是
一、在submit任务加上限制,如果inUse为true,才让put任务进阻塞队列里面。这样会在我上述提到可能出现的问题下,工作者线程会执行完所有队列里面的任务,然后再从break退出。

@Override
  public void submit(Runnable task) throws InterruptedException {
  	if(inUse){
    	channel.put(task);
    	reservations.incrementAndGet();
	}
  }

二、解决语句五可能出现的中断标记被清除的问题,即重新标记,让下次循环直接从channel.take()退出

try {
	task.run();   //---语句五---真正的任务逻辑
} catch (InterruptedException e) {
	this.interrupt(); // 把语句五可能清除的中断标记重新设置为true
} catch (Throwable e) {
	e.printStackTrace();
}

不知道我的理解的对不对。。。求大佬指出一下问题,跪谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值