在打开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() 了
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流。