线程之Timeout

线程之Timeout

Timeout ,我之前的理解,如果报了TimeoutException,不就意味着程序运行的规定时间到了,程序要结束了。但是再琢磨一下,怎么做呀?怎么定一个时间,等时间结束,程序就结束。

看过很多程序结束的方式,其中一种是获得程序运行的线程,然后,调用线程中断命令,当程序中检测到中断指令时,抛出异常,终止程序。等一下… 检测到中断指令,怎么才能时刻检测到中断指令,写个死循环?如下面代码

	public void run(){
		 try {
            for(;;){
                if(Thread.interrupted()){
                   throw new InterruptedException();
                }
                runAnotherCode();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("Stop"+e);
        }
	}

这明显有大病,runAnotherCode() 只要跑一次,跑的时候,怎么随时检测中断,以致于将runAnotherCode() 程序停掉。

好吧,冥思苦想,不得解,看看书吧,好家伙,在《 Java 并发编程实战》里面有介绍——任务的取消。其中,直接给出,Java 没有提供任何机制来安全地终止线程,但是它提供了中断,这是一种协作机制,能够使一个线程终止另一个线程的当前工作。在回想,之前的线程或线程池的退出,都是优雅的关闭,而不是暴力的停掉当前任务,线程退出。

ok,那么这个TimeoutException,是指哪里的Timeout ,显然,和我之前的理解是冲突的,书中提供Future来实现取消

public static void timedRun(Runnable r,long timeout,TimeUnit unit){
	Future<?> task=taskExec.submit(r);
	try{
		task.get(timeout,unit);
		
	}catch(TimeoutException e){
		// 接下来任务将被取消
	}catchExecutionException e){
		//如果在任务中抛出异常,那么重新抛出该异常
		throw launderThrowable(e.getCause());
	}finally{
		//如果任务已经结束,那么执行取消操作也不会带来任何影响
		task.cancel(true);
		//如果任务正在运行,那么将被中断。这里中断是指 运行任务的线程将被中断,
		//至于线程是否响应中断,以及如何响应中断是另外的事情了。
	}
}

看了这段代码,你说这个Timeout 到底是哪里的Timeout,是的确有可能将任务取消了,但也有可能任务正运行呢,所以,这个的Timeout,就是你查这个运行任务的结果,如果没在规定时间内查出来,就Timeout。这对任务是否直接停止可不负责任。
好吧,再来看看get(long timeout,TimeUnit unit) 在future 中的源码吧

//这里是FutureTask 类
// state 是FutureTask的状态, 小于COMPLETING(工作中)的状态是NEW (创建)

 public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        //如果状态小于等于(工作中),且在等了timeout时间内,状态依然小于等于(工作中),那么抛出TimeoutException
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
            //这里是对不同状态做出回应
        return report(s);
    }

好的,再来看看awaitDone(…)方法
这本质是对应多个线程都调用了该方法,怎么办?
所以你会看到WaitNode,并对ta 产生疑惑,ta就是将线程排队

//这是WaitNode 类的方法
 static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }
private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
        // 响应中断,去掉节点q
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
            //用链表将所有的WaitNode,链在一起。
            //注意这个用法,这里的waiters是头节点对象,q.next=waiters,q指向waiters对象,
            //同时,又将waiters的指针指向q
            /*
            获取直接地址
             waitersOffset = UNSAFE.objectFieldOffset (k.getDeclaredField("waiters"));
            like
                 waiters 代表对象
                 waitersOffset 代表指针
                 现在q的next 指向了waiters的对象,
                 然后,waiterOffset 指向了q
                 比如 一开始 waiters为null
                 现在 q.next=null
                 然后,waiters=q。
                 就是往头节点不断新增节点
                 而头节点永远指向最新的节点
            */
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
            else if (timed) {
            //计算时间
                nanos = deadline - System.nanoTime();
                //时间到了。
                if (nanos <= 0L) {
                //去掉节点q
                    removeWaiter(q);
                    //返回现有状态
                    return state;
                }
                //时间没到,那就让线程休息,
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

其中的removeWaiter(…),就是将node 的thread 设置为null。

 private void removeWaiter(WaitNode node) {
        if (node != null) {
            node.thread = null;
            retry:
            for (;;) {          // restart on removeWaiter race
                for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                    s = q.next;
                    // q.thread ==null,则pred 就是前一个WaitNode
                    if (q.thread != null)
                        pred = q;
                     // 如果pred 不为null,
                    else if (pred != null) {
                        //pred的下一个节点就是s,去掉了当前q
                        //正是由于链表的新增只发生在头节点,这个才能安全的删掉q
                        pred.next = s;
                        //如果pred 也被设置为null,那就从retry循环一次
                        if (pred.thread == null) // check for race
                            continue retry;
                    }
                    //如果pred为null,且 q.thead==null,那说明q是头节点啊。
                    //那么就将waiters 赋予s的值,自然就去掉了q。
                    //头节点发生竞争,所有,这个要用cas
                    else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,q, s))
                        continue retry;
                }
                break;
            }
        }
    }

好了。done。源码对细节的把控真的很到位啊。
从源码也可以看出,Timeout是针对查询结果的线程抛出了TimeoutException。线程去查询是否有结果,没有就park(nanos),然后再去查询是否有结果,还没有且时间到了抛出TimeoutException。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值