AbstractInterruptibleChannel接口定义

本文介绍Java NIO中的AbstractInterruptibleChannel类,它允许异步关闭和中断阻塞IO操作。通过begin和end方法配合使用,实现IO操作的异步关闭和中断。
Channel接口定义:[url]http://donald-draper.iteye.com/blog/2369111[/url]
前面看了一下通道接口的定义,只要是为了从根本上理解通道,今天来看一下可异步关闭
和中断阻塞IO操作线程的通道接口定义AbstractInterruptibleChannel,下面是ServerSocketChannel实现的类和接口结构树:
//ServerSocketChannel
public abstract class ServerSocketChannel
extends AbstractSelectableChannel
implements NetworkChannel


//AbstractSelectableChannel
public abstract class AbstractSelectableChannel
extends SelectableChannel


//SelectableChannel
public abstract class SelectableChannel
extends AbstractInterruptibleChannel
implements Channel

从继承和实现树,我们可以看到可选择通道,实际上继承了AbstractInterruptibleChannel,
这就是写这篇文章的原因,下面从源码及Java DOC来分析:

package java.nio.channels.spi;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.nio.channels.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.nio.ch.Interruptible;
/**
* Base implementation class for interruptible channels.
*
AbstractInterruptibleChannel是可中断通道实现类的基础。
* This class encapsulates the low-level machinery required to implement
* the asynchronous closing and interruption of channels. A concrete channel
* class must invoke the {@link #begin begin} and {@link #end end} methods
* before and after, respectively, invoking an I/O operation that might block
* indefinitely. In order to ensure that the {@link #end end} method is always
* invoked, these methods should be used within a
* <tt>try</tt> ... <tt>finally</tt> block: <a name="be">
*
AbstractInterruptibleChannel封装了异步关闭和中断通道的底层操作。具体的通道类,
相对于调用不确定的阻塞IO时,必须调用#begin和#end,即执行通道的IO操作之前调用begin方法
,在执行完后调用end方法。为了保证end的总是被执行,方法应为放在一个try语句块的finally中。
下面是一个简单的实例:
* <blockquote><pre>
* boolean completed = false;
* try {
* begin();
* completed = ...; // Perform blocking I/O operation
* return ...; // Return result
* } finally {
* end(completed);
* }</pre></blockquote>
*
* <p> The <tt>completed</tt> argument to the {@link #end end} method tells
* whether or not the I/O operation actually completed, that is, whether it had
* any effect that would be visible to the invoker. In the case of an
* operation that reads bytes, for example, this argument should be
* <tt>true</tt> if, and only if, some bytes were actually transferred into the
* invoker's target buffer.
*
end方法中的boolean参数completed,用于表示一个IO操作实际上是否完成,即对调用者可见。
在一个读操作中,当且仅当读操作从通道读取数据到调用者的缓存区中时,参数为true,即
channel.read(buf),从通道读取数据到缓存区。
* <p> A concrete channel class must also implement the {@link
* #implCloseChannel implCloseChannel} method in such a way that if it is
* invoked while another thread is blocked in a native I/O operation upon the
* channel then that operation will immediately return, either by throwing an
* exception or by returning normally. If a thread is interrupted or the
* channel upon which it is blocked is asynchronously closed then the channel's
* {@link #end end} method will throw the appropriate exception.
*
一个具体的通道类必须实现#implCloseChannel方法,如果其他线程因本地IO的操作阻塞在通道中,
此方法调用时,相应的IO操作立刻返回或,抛出异常,或正常返回。如果线程中断或阻塞在通道中,
异步关闭通道,end方法将会抛出一个相关的异常。
* <p> This class performs the synchronization required to implement the {@link
* java.nio.channels.Channel} specification. Implementations of the {@link
* #implCloseChannel implCloseChannel} method need not synchronize against
* other threads that might be attempting to close the channel.

*
如果一个具体的通道为同步的需要实现Channel接口。implCloseChannel方法的实现不需要
对其尝试关闭通道的线程进行同步。
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @since 1.4
*/

public abstract class AbstractInterruptibleChannel
implements Channel, InterruptibleChannel
{

private final Object closeLock = new Object();//关闭锁
private volatile boolean open = true;

/**
* Initializes a new instance of this class.
*/
protected AbstractInterruptibleChannel() { }

/**
* Closes this channel.
*
* If the channel has already been closed then this method returns
* immediately. Otherwise it marks the channel as closed and then invokes
* the {@link #implCloseChannel implCloseChannel} method in order to
* complete the close operation.

*
如果一个通道已经被关闭,方法将会立即关闭。否则,将标记通道已经关闭,
然后调用#implCloseChannel方法完成关闭操。
* @throws IOException
* If an I/O error occurs
*/
public final void close() throws IOException {
synchronized (closeLock) {
if (!open)
return;
open = false;
implCloseChannel();
}
}

/**
* Closes this channel.
*
* This method is invoked by the {@link #close close} method in order
* to perform the actual work of closing the channel. This method is only
* invoked if the channel has not yet been closed, and it is never invoked
* more than once.
*
当close方法调用时,将调用implCloseChannel完成实际的关闭通道工作。此方仅在通道还没
完全关闭时调用,此方只会被调用一次。
* <p> An implementation of this method must arrange for any other thread
* that is blocked in an I/O operation upon this channel to return
* immediately, either by throwing an exception or by returning normally.
*

*
此方的实现,必须通知其他阻塞在通道IO操作的线程,立即返回,或抛出异常,或正常返回。
* @throws IOException
* If an I/O error occurs while closing the channel
*/
protected abstract void implCloseChannel() throws IOException;

public final boolean isOpen() {
return open;
}


// -- Interruption machinery --

private Interruptible interruptor;//中断处理器
private volatile Thread interrupted;//中断IO阻塞操作的线程

/**
* Marks the beginning of an I/O operation that might block indefinitely.
*
标记一个可能会阻塞的IO操作的开始
* This method should be invoked in tandem with the {@link #end end}
* method, using a <tt>try</tt> ... <tt>finally</tt> block as
* shown [url=#be]above[/url], in order to implement asynchronous
* closing and interruption for this channel.

begin方法应该与end放配合使用,我们一般用一个try语句块中,上面已经给出实例,
主要是为了实现异步的通道关闭和线程中断。
*/
protected final void begin() {
//如果中断处理器为null,创建一个中断处理器,并在中断线程时,记录记录线程
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
if (!open)
return;
open = false;
//记录中断IO操作阻塞线程的线程
interrupted = target;
try {
//关闭实际通道
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
Thread me = Thread.currentThread();
//如果阻塞的IO线程已经中断,则记录中断阻塞IO操作线程的线程,
//以便end方法判断,是否抛出ClosedByInterruptException
if (me.isInterrupted())
interruptor.interrupt(me);
}

/**
* Marks the end of an I/O operation that might block indefinitely.
*
标志一个可能阻塞IO操作的结束
* This method should be invoked in tandem with the {@link #begin
* begin} method, using a <tt>try</tt> ... <tt>finally</tt> block
* as shown [url=#be]above[/url], in order to implement asynchronous
* closing and interruption for this channel.

*
* @param completed
* <tt>true</tt> if, and only if, the I/O operation completed
* successfully, that is, had some effect that would be visible to
* the operation's invoker
*
* @throws AsynchronousCloseException
* If the channel was asynchronously closed
*
* @throws ClosedByInterruptException
* If the thread blocked in the I/O operation was interrupted
*/
protected final void end(boolean completed)
throws AsynchronousCloseException
{
blockedOn(null);
Thread interrupted = this.interrupted;
//如果中断线程为当前线程,则中断线程interrupted置空,抛出ClosedByInterruptException
if (interrupted != null && interrupted == Thread.currentThread()) {
interrupted = null;
throw new ClosedByInterruptException();
}
//如果通道已关闭,且IO操作为完成,则抛出AsynchronousCloseException
if (!completed && !open)
throw new AsynchronousCloseException();
}


// -- sun.misc.SharedSecrets --
static void blockedOn(Interruptible intr) { // package-private
sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(),
intr);
}
}

[size=medium][b]总结:[/b][/size]
[color=blue]AbstractInterruptibleChannel是一个可以异步关闭和中断IO阻塞线程的通道,所有具体的通道实现,如果想要可以异步关闭和中断,必须实现此类。AbstractInterruptibleChannel内部有一个Open布尔值用于表示通道是否打开。在通道关闭时调用implCloseChannel,implCloseChannel方法完成实际的关闭通道工作。有个中断处理器用于记录中断阻塞IO操作线程的线程,完成实际的关闭通道工作。有一组协调方法为begin和end方法,一般在一个可能阻塞的IO操作的开始调用begin,之后调用end方法,这些操作一般用一个try语句块,组合使用。begin方法主要初始化中断处理器,end方法根据IO操作是否完成和Open状态,及中断线程处理器,中断线程判断是抛出AsynchronousCloseException异常还是ClosedByInterruptException。[/color]


//Interruptible
package sun.nio.ch;
public interface Interruptible
{
public abstract void interrupt(Thread thread);
}


//InterruptibleChannel,可异步关闭和中断的通道
package java.nio.channels;
import java.io.IOException;
/**
* A channel that can be asynchronously closed and interrupted.
*
InterruptibleChannel表示一个可以异步关闭和中断的通道。
* A channel that implements this interface is <i>asynchronously
* closeable:</i> If a thread is blocked in an I/O operation on an
* interruptible channel then another thread may invoke the channel's {@link
* #close close} method. This will cause the blocked thread to receive an
* {@link AsynchronousCloseException}.
*
实现此接口的通道是异步可关闭:如果一个线程在一个可中断的通道,因为IO操作阻塞,
其他线程可以调用close关闭通道。阻塞的线程将会接受到一个AsynchronousCloseException异常。
* <p> A channel that implements this interface is also <i>interruptible:</i>
* If a thread is blocked in an I/O operation on an interruptible channel then
* another thread may invoke the blocked thread's {@link Thread#interrupt()
* interrupt} method. This will cause the channel to be closed, the blocked
* thread to receive a {@link ClosedByInterruptException}, and the blocked
* thread's interrupt status to be set.
*
实现此接口的通道是可中断的:如果一个线程在一个可中断的通道中,因为IO操作阻塞,
其他线程可以调用阻塞的线程的中断方法Thread#interrupt,中断阻塞线程。中断后通道
将会关闭,阻塞线程将会接受一个ClosedByInterruptException,阻塞线程的中断位将会被
设置。
* <p> If a thread's interrupt status is already set and it invokes a blocking
* I/O operation upon a channel then the channel will be closed and the thread
* will immediately receive a {@link ClosedByInterruptException}; its interrupt
* status will remain set.
*
如果一个线程中断位已经被设置,因IO操作阻塞的线程所在的通道,将会关闭,阻塞线程
将会立刻接受一个ClosedByInterruptException,阻塞线程仍处于中断状态。
* <p> A channel supports asynchronous closing and interruption if, and only
* if, it implements this interface. This can be tested at runtime, if
* necessary, via the <tt>instanceof</tt> operator.
*
InterruptibleChannel是一个异步中断和关闭的通道。在运行时环境中,如果需要,
我们可以通过instanceof方法判断一个通道是否为InterruptibleChannel。
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @since 1.4
*/

public interface InterruptibleChannel
extends Channel
{

/**
* Closes this channel.
*
* <p> Any thread currently blocked in an I/O operation upon this channel
* will receive an {@link AsynchronousCloseException}.
关闭通道时,任何因IO操作在通道中,阻塞的线程将会接受一个AsynchronousCloseException
* <p> This method otherwise behaves exactly as specified by the {@link
* Channel#close Channel} interface.

*
此方其他方面与Channel#close的方法作用基本相同。
* @throws IOException If an I/O error occurs
*/
public void close() throws IOException;
}
15:03:48.535 I mWNT: t=0xb400006efdd53700 mBlastBufferQueue=0xb400006fe3786e00 fn= 5998 HdrRenderState mRenderHdrSdrRatio=1.0 caller= android.view.ViewRootImpl$9.onFrameDraw:6276 android.view.ViewRootImpl$3.onFrameDraw:2440 android.view.ThreadedRenderer$1.onFrameDraw:761 15:03:48.647 E 备份过程中发生错误 java.nio.channels.ClosedByInterruptException at java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:200) at sun.nio.ch.FileChannelImpl.size(FileChannelImpl.java:362) at com.kotei.overseas.navi.update.singlecopydirectory.mediumFileCopy(singlecopydirectory.java:176) at com.kotei.overseas.navi.update.singlecopydirectory.copyFileWithProgress(singlecopydirectory.java:135) at com.kotei.overseas.navi.update.singlecopydirectory.copyDirectoryInternal(singlecopydirectory.java:119) at com.kotei.overseas.navi.update.singlecopydirectory.copyDirectoryInternal(singlecopydirectory.java:117) at com.kotei.overseas.navi.update.singlecopydirectory.copyDirectoryInternal(singlecopydirectory.java:117) at com.kotei.overseas.navi.update.singlecopydirectory.copyDirectoryInternal(singlecopydirectory.java:117) at com.kotei.overseas.navi.update.singlecopydirectory.copyDirectoryInternal(singlecopydirectory.java:117) at com.kotei.overseas.navi.update.singlecopydirectory.copyDirectoryInternal(singlecopydirectory.java:117) at com.kotei.overseas.navi.update.singlecopydirectory.copyDirectoryWithProgress(singlecopydirectory.java:80) at com.kotei.overseas.navi.update.USBOfflineUpdater$UpdateTask.doInBackground(USBOfflineUpdater.java:719) at com.kotei.overseas.navi.update.USBOfflineUpdater$UpdateTask.doInBackground(USBOfflineUpdater.java:676) at android.os.AsyncTask$3.call(AsyncTask.java:394) at java.util.concurrent.FutureTask.run(FutureTask.java:264) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644) at java.lang.Thread.run(Thread.java:1012) 15:03:51.513 I call setFrameRateCategory for touch hint category=no preference, reason=boost timeout, vri=VRI[MainActivity]@79b82fd 解释这个报错会带来什么
最新发布
09-03
这个报错是: ``` java.nio.channels.ClosedByInterruptException at java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:200) at sun.nio.ch.FileChannelImpl.size(FileChannelImpl.java:362) at com.kotei.overseas.navi.update.singlecopydirectory.mediumFileCopy(singlecopydirectory.java:176) at com.kotei.overseas.navi.update.singlecopydirectory.copyFileWithProgress(singlecopydirectory.java:135) at com.kotei.overseas.navi.update.singlecopydirectory.copyDirectoryInternal(singlecopydirectory.java:119) ... at com.kotei.overseas.navi.update.USBOfflineUpdater$UpdateTask.doInBackground(USBOfflineUpdater.java:719) ``` --- ## 🧨 报错含义解释 ### `ClosedByInterruptException` 是什么? 这是 **Java NIO 包中的异常**,表示一个 **通道(Channel)被中断了**。 ### 为什么会抛出这个异常? 当你调用了 `Thread.interrupt()`,并且当前线程正在执行 **NIO 的阻塞操作**(如 `FileChannel.read()`、`FileChannel.write()`、`FileChannel.size()`)时,Java 会自动关闭通道并抛出 `ClosedByInterruptException`。 --- ## 📌 报错发生在哪一步? 根据堆栈信息,报错发生在以下流程: ``` singlecopydirectory.mediumFileCopy() └── copyFileWithProgress() └── copyDirectoryInternal() └── copyDirectoryWithProgress() └── doInBackground() in UpdateTask ``` 说明你正在执行 **备份操作**,在复制文件过程中调用了 `FileChannel.size()`,而该操作被中断了。 --- ## 🧩 这个错误会带来哪些后果? | 影响 | 说明 | |------|------| | ✅ **备份操作中断** | 当前备份流程被中断,无法完成完整的数据备份。 | | 🚫 **可能导致数据不一致** | 如果备份只完成了一部分,后续回滚可能失败或不完整。 | | 🚫 **临时文件未清理** | 如果没有在异常处理中删除未完成的备份文件,会导致磁盘残留。 | | ✅ **触发回滚机制** | 如果你实现了 `handleFailure()`,会进入回滚逻辑。 | | 🚫 **用户体验受损** | 用户看到更新失败提示,可能需要重新插入 USB 或重启应用。 | --- ## 🧱 代码风险点分析 你的代码中存在以下潜在问题: ### 🔴 1. **没有捕获 `ClosedByInterruptException`** 在 `mediumFileCopy()` 或 `copyFileWithProgress()` 中,你可能直接调用了: ```java FileChannel inChannel = fis.getChannel(); long size = inChannel.size(); // 可能抛出 ClosedByInterruptException ``` 但没有捕获这个异常,导致整个任务中断。 ### 🔴 2. **没有清理中断后的临时文件** 如果中断发生时,部分文件已经写入磁盘,但未被删除,会造成磁盘空间浪费。 --- ## ✅ 正确处理方式 ### ✅ 1. 捕获异常并清理资源 ```java try { // 执行文件复制或读取 } catch (ClosedByInterruptException e) { Log.w(TAG, "文件操作被中断,清理临时文件"); FileUtils.deleteFile(tempFile); // 删除未完成的文件 Thread.currentThread().interrupt(); // 保留中断状态 return false; } ``` ### ✅ 2. 在 `finally` 块中关闭资源 ```java finally { if (inChannel != null && inChannel.isOpen()) { try { inChannel.close(); } catch (IOException e) { Log.e(TAG, "关闭 inChannel 失败", e); } } } ``` ### ✅ 3. 在 `doInBackground()` 中统一处理中断 ```java @Override protected Integer doInBackground(Void... voids) { try { // 执行备份、复制等操作 } catch (ClosedByInterruptException e) { Log.w(TAG, "任务被中断"); Thread.currentThread().interrupt(); return ERROR_USER_CANCELED; } catch (Exception e) { Log.e(TAG, "发生异常", e); return ERROR_UNEXPECTED; } finally { cleanupResources(); // 清理未完成的备份文件 } } ``` --- ## ✅ 总结:这个报错会带来什么? | 问题 | 描述 | |------|------| | **中断发生** | 线程被调用 `interrupt()`,NIO 操作被中断 | | **备份失败** | 当前备份流程中断,无法完成 | | **资源未释放** | 如果未处理异常,可能导致临时文件残留 | | **回滚机制被触发** | 如果有回滚逻辑,会尝试恢复 | | **用户体验下降** | 用户看到更新失败提示,需重新操作 | --- ## ✅ 建议改进点 | 改进项 | 建议 | |--------|------| | 捕获 `ClosedByInterruptException` | 避免任务直接崩溃 | | 使用 `try-with-resources` | 自动关闭流 | | 清理中断后的临时文件 | 避免残留 | | 在 `onCancelled()` 中释放资源 | 替代直接调用 `notifyListener()` | | 日志中记录中断状态 | 便于调试和定位问题 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值