Netty代码里是如何优雅地中断(interrupt)线程

在打开I/O流或者网络流长时间读取数据时,如果要中间停掉线程,我们需要使用线程的interrupt方法来中断线程。

Netty里有个很优雅的实现,类似于Spring的事务的控制,有开始事务和回滚事务,如果执行发生错误,可以抛出异常回滚事务。

先来上代码吧(请耐心看完这两段代码):

这个是参考Netty编写的抽象类
package com.renjia.extend.www.course1;

import sun.nio.ch.Interruptible;

/**
 * @author renjia
 *
 */
public abstract class AbstractInterrupt {
	private Object closeLock = new Object();
	private volatile boolean interrupted = false;
	private Interruptible interruptor = null;

	protected final void begin() {
		if (interruptor==null) {
			synchronized (closeLock) {
				interruptor = new Interruptible() {
					@Override
					public void interrupt() {
						interrupted=true;
						AbstractInterrupt.this.closeChannel();
					}
				};
			}
		}

		blockOn( interruptor );
	}

	protected abstract void closeChannel();

	protected final void end() {
		blockOn( null );
		interrupted=false;
	}

	static void blockOn( Interruptible interruptible ) {
		sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(), interruptible);
	}
}


注意这行代码是什么意思呢?

sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(), interruptible);

首先要看Thread类里的一个方法:

    /* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code
     */
    void blockedOn(Interruptible b) {
	synchronized (blockerLock) {
	    blocker = b;
	}
    }

从注释里看,这个方法是被sun.misc.SharedSecrets设置的,也就是sun.misc.SharedSecrets.getJavaLangAccess().blockedOn这个方法,将中断的业务和线程绑定到一起。

绑定到这里是做什么用的呢?

再看Thread类里的另一个方法:

    public void interrupt() {
	if (this != Thread.currentThread())
	    checkAccess();

	synchronized (blockerLock) {
	    Interruptible b = blocker;
	    if (b != null) {
		interrupt0();		// Just to set the interrupt flag
		b.interrupt();
		return;
	    }
	}
	interrupt0();
    }

这段代码里的 Interruptible b = blocker 就是刚才设置好的对象,b.interrupt() 调用的就是Interruptible interruptor 接口的实现方法interrupt() 了


再看第二段代码,这段代码是上面的抽象类的实现类,通过此类的main方法来验证结果
package com.renjia.extend.www.course1;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @author renjia
 *
 */
public class FileContentReader extends AbstractInterrupt{
	BufferedInputStream buffStream = null;
	public void execute() {
		try {
			long beginMills = System.currentTimeMillis();
			begin();
			String file = "e:/data/test.log";
			buffStream = new BufferedInputStream(new FileInputStream(file));
			int read = 0;
			while( (read = buffStream.read())>=0 ) {
				
				int i=0;
				while( i<=1000 ) {
					System.out.println( read + (System.currentTimeMillis()-beginMills) + "ms" );
					i++;
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			end();
		}
	}
	
	@Override
	protected void closeChannel() {
		if (buffStream!=null) {
			try {
				System.out.println("关闭了buffStream.");
				buffStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		Thread t = new Thread( new Runnable() {
			
			@Override
			public void run() {
				FileContentReader reader = new FileContentReader();
				reader.execute();
			}
		} );
		
		t.start();
		
		Thread.sleep(10000);
		
		t.interrupt();
	}

}

代码里的 begin()方法将关闭方法closeChannel()与线程绑定,end()方法是解除这个绑定。


最后的main方法在主线程里等待子线程执行10秒后,调用子线程的终端方法,其实就是调用了closeChannel()方法,关闭I/O流。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值