如何编写响应`Thread.interrupt()`的本机代码?

时间:2015-03-29 15:24:48

标签: java multithreading java-native-interface interrupted-exception

在Java中,所有标准的阻塞方法都可以通过调用Thread.interrupt()来中断,但如果我们有Java绑定包装一个执行自己的I / O的本机库呢?那么本机代码应该如何挂钩到线程并响应对Thread.interrupt()的调用?

1 个答案:

答案 0 :(得分:8)

示例代码

有关完整的文章,包括可运行的示例代码,请参阅https://github.com/NWilson/javaInterruptHook

Thread.interrupt()如何运作?

在Sun的JRE(和OpenJDK)中,interrupt()能够唤醒一些低级操作,例如在监视器上等待(Object.wait()),并提供内部钩子,用于通知中断的更高级代码。这是通过JavaLangAccess.blockedOn()提供的,如果您愿意使用特定于Sun的实施细节,则可以通过sun.misc.SharedSecrets直接通过代码直接调用。

据我所知,只有一种公开的,有文件记录的方式来注册该通知,即使用java.nio.channels.spi.AbstractSelector。这是Selector的部分实现,它可以帮助您将JavaLangAccess.blockedOn()通知连接到选择器的wakeup()方法。

如何实现这一切

实施AbstractSelector制作自己的选择器;大多数方法都不相关,所以只需忽略它们并放入存根来关闭编译器。当您即将输入JNI阻止方法时,请调用AbstractSelector begin()方法,然后在返回JNI调用时调用end()

选择器应在其wakeup()的实现中取消JNI方法。

粗略代码

(有关完整运行示例,请参阅顶部链接的github repo。)

class NativeTask {
    public NativeTask() {}

    public void doTask()
            throws InterruptedException {
        NativeTaskSelector selector = new NativeTaskSelector(this);
        try {
            selector.registerStart();
            doTask0(); // native method
            if (Thread.interrupted())
                    throw new InterruptedException();
        } finally {
            selector.registerEnd();
            try { selector.close(); } catch (IOException impossible) {}
        }
    }
    /* The long-running native operation. */
    native private void doTask0();

    public void wakeupTask() { wakeupTask0(); }
    /* A way to cause the native operation to wake up. */
    native private void wakeupTask0();
}

class NativeTaskSelector extends AbstractSelector {

    protected NativeTaskSelector(NativeTask task_) {
        super(null);
        task = task_;
    }

    public void registerStart() { begin(); }
    public void registerEnd() { end(); }

    final private NativeTask task;

    @Override
    public Selector wakeup() {
        task.wakeupTask();
        return this;
    }

    @Override
    protected void implCloseSelector() throws IOException {
    }

    @Override
    protected SelectionKey register(AbstractSelectableChannel arg0, int arg1,
            Object arg2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Set<SelectionKey> keys() {
        throw new UnsupportedOperationException();
    }

    @Override
    public int select() throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public int select(long arg0) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public int selectNow() throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public Set<SelectionKey> selectedKeys() {
        throw new UnsupportedOperationException();
    }

}