![](https://i-blog.csdnimg.cn/blog_migrate/0df386222c746327c6d63063a322916f.png)
2、SocketChannel 时序图:
![](https://i-blog.csdnimg.cn/blog_migrate/8f607fe85519c5fc12967b3b49de0c6d.png)
private volatile Interruptible blocker;
private final Object blockerLock = new Object();
/* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code
*/
void blockedOn(Interruptible b) {
synchronized (blockerLock) {
blocker = b;
}
}
blockedOn 方法用以设置 blocker 字段
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(this);
return;
}
}
interrupt0();
}
4、在 AbstractInterruptibleChannel 的 begin 方法中:
private Interruptible interruptor;
private volatile Thread interrupted;
/**
* Marks the beginning of an I/O operation that might block indefinitely.
*
* <p> This method should be invoked in tandem with the {@link #end end}
* method, using a <tt>try</tt> ... <tt>finally</tt> block as
* shown <a href="#be">above</a>, in order to implement asynchronous
* closing and interruption for this channel. </p>
*/
protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
if (!open)
return;
open = false;
interrupted = target;
try {
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
Thread me = Thread.currentThread();
if (me.isInterrupted())
interruptor.interrupt(me);
}
给 AbstractInterruptibleChannel 里的 Interruptible 接口类型的成员变量 interruptor 赋予初值,很明显,在赋值语句中,被赋予的值是一个实现了 Interruptible 接口的匿名类。 在这个匿名类的 interrupt 方法中,做了两件事,第一,保存当前被中断的线程是谁,第二,对 Channel 进行了关闭。 接下来,将这个 interruptor 设置给当前调用 begin 的线程。 按照我们前面对 Thread 类的检视我们看见,Thread 的 interrupt 方法中除了常规的 interrupt 操作,还有对线程 Interruptible 接口类型的成员变量 blocker 的 interrupt 方法的调用。所以综合来说,线程在中断时,除了对线程常规的 interrupt 操作-设置一个中断标志位外,完全还有可能对对 Channel 进行了关闭。 在 end 方法中:
protected final void end(boolean completed)
throws AsynchronousCloseException
{
blockedOn(null);//清空线程的blocker字段
Thread interrupted = this.interrupted;
//如果线程被中断,则begin里面初始化的Interruptible对象的interrupt方法里面设置了interrupted 变量为被中断的线程
if (interrupted != null && interrupted == Thread.currentThread()) {
interrupted = null;
throw new ClosedByInterruptException();//如果被中断抛出此异常
}
if (!completed && !open)//如果被close ,抛出此异常
throw new AsynchronousCloseException();
}
![](https://i-blog.csdnimg.cn/blog_migrate/e12fc16df567906f5c367cea308eeaf6.png)
private SelectionKey[] keys = null;
public final SelectionKey register(Selector sel, int ops,
Object att)
throws ClosedChannelException
{
synchronized (regLock) {
if (!isOpen())
throw new ClosedChannelException();
if ((ops & ~validOps()) != 0)
throw new IllegalArgumentException();
if (blocking)
throw new IllegalBlockingModeException();
SelectionKey k = findKey(sel);
if (k != null) {
k.interestOps(ops);
k.attach(att);
}
if (k == null) {
// New registration
synchronized (keyLock) {
if (!isOpen())
throw new ClosedChannelException();
k = ((AbstractSelector)sel).register(this, ops, att);
addKey(k);
}
}
return k;
}
}
首先进行检查,比如通道是否打开、注册键值是否有效、通道是否非阻塞模式等等,然后检查数组 keys 中的 key 能否在 selector 找到,找到则直接修改 key 关注的键值并替换附件, 没找到,则在 selector 中注册这个 key,并加入到内部 SelectionKey 的数组 keys 中。AbstractSelectableChannel 中的其他方法基本都是为 key 的注册服务的,可以自行查阅相关代码。
private static final int ST_UNINITIALIZED = -1;
private static final int ST_UNCONNECTED = 0;
private static final int ST_PENDING = 1;
private static final int ST_CONNECTED = 2;
private static final int ST_KILLPENDING = 3;
private static final int ST_KILLED = 4;
SocketChannel 有生命周期,SocketChannel 会有一个 state 标记当前的状态,默认为-1 表示 ST_UNINITIALIZED(未初始化) 在构造函数最后会将 state 更新为 0(ST_UNCONNECTED,未连接) 调用 connect 连接服务端,连接成功之前更新 state 为 1(ST_PENDING,待连接) 连接成功时会更新 state 为 2(ST_CONNECTED,已连接) 关闭通道时若 I/O 未完成时会将 state 更新为 3(ST_KILLPENDING,待释放) 当关闭通道后,且所有 I/O 已完成,会将 state 更新为 4(ST_KILLED,已释放)
private Socket socket;
同时在具体的读写和连接等网络操作上,依然是由具体的 socket 负责并最终交给 native 方法来实现。 比如连接操作,首先同步读锁和写锁,确保 socket 通道打开,并没有连接;然后检查 socket 地址的正确性与合法性,然后检查当前线程是否有 Connect 方法的访问控制权限, 最后尝试连接 socket 地址。
![](https://i-blog.csdnimg.cn/blog_migrate/31c6b790184f8849b378118551adc9c3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/55219de72f15600612e5d6b1c27a52dc.png)
public static ServerSocketChannel open() throws IOException {
return SelectorProvider.provider().openServerSocketChannel();
}
进入这个类 public abstract class SelectorProviderImpl extends SelectorProvider
public ServerSocketChannel openServerSocketChannel() throws IOException {
return new ServerSocketChannelImpl(this);
}
进入 class ServerSocketChannelImpl extends ServerSocketChannel implements SelChImpl
ServerSocketChannelImpl(SelectorProvider var1) throws IOException {
super(var1);
this.fd = Net.serverSocket(true);
this.fdVal = IOUtil.fdVal(this.fd);
this.state = 0;
}
和 Selector 一样,内部实际是通过 SelectorProvider 来创建的 SocketChannel 的实例。从调用链来说,由 SelectorProviderImpl 的 openServerSocketChannel()返回了一个 ServerSocketChannelImpl 的实例 从 ServerSocketChannelImpl 的实现可以看出,其实 ServerSocketChannelImpl 在内部和 SocketChannelImpl 非常相似,在具体的 accept 和 bind 等网络操作上,依然是由具体的 socket 负责并最终交给 native 方法来实现。
![](https://i-blog.csdnimg.cn/blog_migrate/a63dee3f36d897c22765bafbb26ed085.png)
/*创建选择器的实例*/
selector = Selector.open();
进入:
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
进入:
public static SelectorProvider provider() {
synchronized (lock) {
if (provider != null)
return provider;
return AccessController.doPrivileged(
new PrivilegedAction<SelectorProvider>() {
public SelectorProvider run() {
if (loadProviderFromProperty())
return provider;
if (loadProviderAsService())
return provider;
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}
进入:
/**
* Creates this platform's default SelectorProvider
*/
public class DefaultSelectorProvider {
private static final SelectorProviderImpl INSTANCE;
static {
PrivilegedAction<SelectorProviderImpl> pa = EPollSelectorProvider::new;
INSTANCE = AccessController.doPrivileged(pa);
}
/**
* Prevent instantiation.
*/
private DefaultSelectorProvider() { }
/**
* Returns the default SelectorProvider implementation.
*/
public static SelectorProviderImpl get() {
return INSTANCE;
}
}
或这个版本
![](https://i-blog.csdnimg.cn/blog_migrate/2d23e0ceecc6cb9a88bf61a360ef34f8.png)
可以看见,Linux 下JDK创建的是 EpollSelectorProvider
package sun.nio.ch;
import java.io.IOException;
import java.nio.channels.*;
import java.nio.channels.spi.*;
public class EPollSelectorProvider
extends SelectorProviderImpl
{
public AbstractSelector openSelector() throws IOException {
return new EPollSelectorImpl(this);
}
public Channel inheritedChannel() throws IOException {
return InheritedChannel.getChannel();
}
}
通过 EpollSelectorProvider 的 openSelector() 方法,其实拿到的 Selector 实例是 EpollSelectorImpl。
Selector 基本上都是抽象方法,非抽象的 open()方法我们前面已经介绍过了。 AbstractSelector 中的方法也没有特别值得留意的地方,当然其中也用到了和 AbstractInterruptibleChannel 类似的支持可异步关闭和中断的机制,前面已经讲述过,这里不再重复讲述。所以,关键还在于 SelectorImpl。 SelectorImpl 的原始 Java 文件可以在 jdk\src\share\classes\sun\nio\ch 下找到。 在创建 SelectorImpl 首先会初始化 2 个 HashSet,publicKeys 用于存放所有注册的 SelectionKey,publicSelectedKeys 用于存放已就绪的 SelectionKey。
类图:
类关系:public abstract class AbstractSelector extends Selector
public abstract class SelectorImpl extends AbstractSelector {
protected Set<SelectionKey> selectedKeys = new HashSet();
protected HashSet<SelectionKey> keys = new HashSet();
private Set<SelectionKey> publicKeys;
private Set<SelectionKey> publicSelectedKeys;
protected SelectorImpl(SelectorProvider var1) {
super(var1);
if (Util.atBugLevel("1.4")) {
this.publicKeys = this.keys;
this.publicSelectedKeys = this.selectedKeys;
} else {
this.publicKeys = Collections.unmodifiableSet(this.keys);
this.publicSelectedKeys = Util.ungrowableSet(this.selectedKeys);
}
}
11、EpollSelectorImpl
class EPollSelectorImpl extends SelectorImpl {
// maximum number of events to poll in one call to epoll_wait
private static final int NUM_EPOLLEVENTS = Math.min(IOUtil.fdLimit(), 1024);
// epoll file descriptor
private final int epfd;
// address of poll array when polling with epoll_wait
private final long pollArrayAddress;
// eventfd object used for interrupt
private final EventFD eventfd;
// maps file descriptor to selection key, synchronize on selector
private final Map<Integer, SelectionKeyImpl> fdToKey = new HashMap<>();
// pending new registrations/updates, queued by setEventOps
private final Object updateLock = new Object();
private final Deque<SelectionKeyImpl> updateKeys = new ArrayDeque<>();
// interrupt triggering and clearing
private final Object interruptLock = new Object();
private boolean interruptTriggered;
或者此版本
![](https://i-blog.csdnimg.cn/blog_migrate/19fa50b000bdb3ac2aa8640764dcc66f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/47d67d792291840299d31e5bdbd5818a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/01bb9a2cdca6fb3142ce1043be3da4d0.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7d57d4ede6c10a7b138d69613467ce18.png)
/*注册事件,表示关心客户端连接*/
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
时会发生什么呢?通过对代码的追踪,我们可以发现,执行的注册代码是由AbstractSelectableChannel 中的 register 方法,调用了 Selector 的 register 方法进行注册,这是个抽象方法,在 SelectorImpl 中有具体实现:
protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
if (!(var1 instanceof SelChImpl)) {
throw new IllegalSelectorException();
} else {
SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
var4.attach(var3);
synchronized(this.publicKeys) {
this.implRegister(var4);
}
var4.interestOps(var2);
return var4;
}
}
protected abstract void implRegister(SelectionKeyImpl var1);
但是很明显,执行具体的注册的 implRegister 方法也是个抽象方法,具体的注册实现在 EpollSelectorImpl 中:
win环境实现:
//class WindowsSelectorImpl extends SelectorImpl 类实现
@Override
protected void implRegister(SelectionKeyImpl ski) {
ensureOpen();
synchronized (updateLock) {
newKeys.addLast(ski);
}
}
linux实现:
![](https://i-blog.csdnimg.cn/blog_migrate/93ae38b6759feee4e177aa3a016cd4a2.png)
public int select(long var1) throws IOException {
if (var1 < 0L) {
throw new IllegalArgumentException("Negative timeout");
} else {
return this.lockAndDoSelect(var1 == 0L ? -1L : var1);
}
}
public int select() throws IOException {
return this.select(0L);
}
public int selectNow() throws IOException {
return this.lockAndDoSelect(0L);
}
可见最终都是 lockAndDoSelect 方法实现的
protected abstract int doSelect(long var1) throws IOException;
private int lockAndDoSelect(long var1) throws IOException {
synchronized(this) {
if (!this.isOpen()) {
throw new ClosedSelectorException();
} else {
int var10000;
synchronized(this.publicKeys) {
synchronized(this.publicSelectedKeys) {
var10000 = this.doSelect(var1);
}
}
return var10000;
}
}
}
doSelect 是个抽象方法,所以最终会调用具体 SelectorImpl 的 doSelect,比如 EpollSelectorImpl 中:
@Override
protected int doSelect(Consumer<SelectionKey> action, long timeout)
throws IOException
{
assert Thread.holdsLock(this);
// epoll_wait timeout is int
int to = (int) Math.min(timeout, Integer.MAX_VALUE);
boolean blocking = (to != 0);
boolean timedPoll = (to > 0);
int numEntries;
processUpdateQueue();
processDeregisterQueue();
try {
begin(blocking);
do {
long startTime = timedPoll ? System.nanoTime() : 0;
numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, to);
if (numEntries == IOStatus.INTERRUPTED && timedPoll) {
// timed poll interrupted so need to adjust timeout
long adjust = System.nanoTime() - startTime;
to -= TimeUnit.MILLISECONDS.convert(adjust, TimeUnit.NANOSECONDS);
if (to <= 0) {
// timeout expired so no retry
numEntries = 0;
}
}
} while (numEntries == IOStatus.INTERRUPTED);
assert IOStatus.check(numEntries);
} finally {
end(blocking);
}
processDeregisterQueue();
return processEvents(numEntries, action);
}
继续调,processEvents方法:
private int processEvents(int numEntries, Consumer<SelectionKey> action)
throws IOException
{
assert Thread.holdsLock(this);
boolean interrupted = false;
int numKeysUpdated = 0;
for (int i=0; i<numEntries; i++) {
long event = EPoll.getEvent(pollArrayAddress, i);
int fd = EPoll.getDescriptor(event);
if (fd == eventfd.efd()) {
interrupted = true;
} else {
SelectionKeyImpl ski = fdToKey.get(fd);
if (ski != null) {
int rOps = EPoll.getEvents(event);
numKeysUpdated += processReadyEvents(rOps, ski, action);
}
}
}
if (interrupted) {
clearInterrupt();
}
return numKeysUpdated;
}
win环境 WindowsSelectorImpl extends SelectorImpl 类中:
@Override
protected int doSelect(Consumer<SelectionKey> action, long timeout)
throws IOException
{
assert Thread.holdsLock(this);
this.timeout = timeout; // set selector timeout
processUpdateQueue();
processDeregisterQueue();
if (interruptTriggered) {
resetWakeupSocket();
return 0;
}
// Calculate number of helper threads needed for poll. If necessary
// threads are created here and start waiting on startLock
adjustThreadsCount();
finishLock.reset(); // reset finishLock
// Wakeup helper threads, waiting on startLock, so they start polling.
// Redundant threads will exit here after wakeup.
startLock.startThreads();
// do polling in the main thread. Main thread is responsible for
// first MAX_SELECTABLE_FDS entries in pollArray.
try {
begin();
try {
subSelector.poll();
} catch (IOException e) {
finishLock.setException(e); // Save this exception
}
// Main thread is out of poll(). Wakeup others and wait for them
if (threads.size() > 0)
finishLock.waitForHelperThreads();
} finally {
end();
}
// Done with poll(). Set wakeupSocket to nonsignaled for the next run.
finishLock.checkForException();
processDeregisterQueue();
int updated = updateSelectedKeys(action);
// Done with poll(). Set wakeupSocket to nonsignaled for the next run.
resetWakeupSocket();
return updated;
}
具体内部主要执行 3 件事:
![](https://i-blog.csdnimg.cn/blog_migrate/f1400c4b53561cce6e7376af4339c92f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/aa1e664d59390d45b901cd5f78f1b170.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e04357996c6d2836fd746c6367913709.png)