在使用Selector時首先需要通過靜態方法open建立Selector物件
1 public static Selector open() throws IOException { 2 return SelectorProvider.provider().openSelector(); 3 }
可以看到首先是呼叫SelectorProvider的靜態方法provider,得到一個Selector的提供者
1 public static SelectorProvider provider() { 2 synchronized (lock) { 3 if (provider != null) 4 return provider; 5 return AccessController.doPrivileged( 6 new PrivilegedAction<SelectorProvider>() { 7 public SelectorProvider run() { 8 if (loadProviderFromProperty()) 9 return provider; 10 if (loadProviderAsService()) 11 return provider; 12 provider = sun.nio.ch.DefaultSelectorProvider.create(); 13 return provider; 14 } 15 }); 16 } 17 }
這段程式碼的邏輯也比較簡單,首先判斷provider是否已經產生,若已經產生,則直接返回現有的;若沒有,則需要呼叫AccessController的靜態方法doPrivileged,該方法是一個native方法,就不說了;可以看到在實現的PrivilegedAction介面中的run方法,做了三次判斷:
第一次是根據是系統屬性,使用ClassLoader類載入:
1 private static boolean loadProviderFromProperty() { 2 String cn = System.getProperty("java.nio.channels.spi.SelectorProvider"); 3 if (cn == null) 4 return false; 5 try { 6 Class<?> c = Class.forName(cn, true, 7 ClassLoader.getSystemClassLoader()); 8 provider = (SelectorProvider)c.newInstance(); 9 return true; 10 } catch (ClassNotFoundException x) { 11 throw new ServiceConfigurationError(null, x); 12 } catch (IllegalAccessException x) { 13 throw new ServiceConfigurationError(null, x); 14 } catch (InstantiationException x) { 15 throw new ServiceConfigurationError(null, x); 16 } catch (SecurityException x) { 17 throw new ServiceConfigurationError(null, x); 18 } 19 }
先獲取鍵值為"java.nio.channels.spi.SelectorProvider"的屬性,若沒有,則直接返回false;若設定了,則需要使用載入器直接載入系統屬性設定的java.nio.channels.spi.SelectorProvider的實現類,再通過反射機制直接產生例項物件並賦值給靜態成員provider,最後返回true。
第二次使用ServiceLoader載入:
1 private static boolean loadProviderAsService() { 2 ServiceLoader<SelectorProvider> sl = 3 ServiceLoader.load(SelectorProvider.class, 4 ClassLoader.getSystemClassLoader()); 5 Iterator<SelectorProvider> i = sl.iterator(); 6 for (;;) { 7 try { 8 if (!i.hasNext()) 9 return false; 10 provider = i.next(); 11 return true; 12 } catch (ServiceConfigurationError sce) { 13 if (sce.getCause() instanceof SecurityException) { 14 // Ignore the security exception, try the next provider 15 continue; 16 } 17 throw sce; 18 } 19 } 20 }
有關ServiceLoader的載入過程可以看我的上一篇部落格【Java】ServiceLoader原始碼分析,在這裡我就不累贅了。
該方法呼叫ServiceLoader的load載入在"META-INF/services/"路徑下指明的SelectorProvider.class的實現類(其實是懶載入,在迭代時才真正載入)得到ServiceLoader物件,通過該物件的帶迭代器,遍歷這個迭代器;可以看到若是迭代器不為空,則直接返回迭代器儲存的第一個元素,即第一個被載入的類的物件,並賦值給provider,返回true;否則返回false;
第三次是使用的預設的SelectorProvider(windows環境為例):
1 public class DefaultSelectorProvider { 2 private DefaultSelectorProvider() { 3 } 4 5 public static SelectorProvider create() { 6 return new WindowsSelectorProvider(); 7 } 8 }
可以看到直接返回了WindowsSelectorProvider賦值給provider ;
此時provider無論如何都已經有了,接下來就是呼叫provider的openSelector方法。
WindowsSelectorProvider的openSelector方法:
1 public class WindowsSelectorProvider extends SelectorProviderImpl { 2 public WindowsSelectorProvider() { 3 } 4 5 public AbstractSelector openSelector() throws IOException { 6 return new WindowsSelectorImpl(this); 7 } 8 }
可以看到僅僅是產生了WindowsSelectorImpl:
1 WindowsSelectorImpl(SelectorProvider var1) throws IOException { 2 super(var1); 3 this.wakeupSourceFd = ((SelChImpl)this.wakeupPipe.source()).getFDVal(); 4 SinkChannelImpl var2 = (SinkChannelImpl)this.wakeupPipe.sink(); 5 var2.sc.socket().setTcpNoDelay(true); 6 this.wakeupSinkFd = var2.getFDVal(); 7 this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, 0); 8 }
WindowsSelectorImpl首先呼叫父類SelectorImpl的構造方法:
1 protected Set<SelectionKey> selectedKeys = new HashSet(); 2 protected HashSet<SelectionKey> keys = new HashSet(); 3 private Set<SelectionKey> publicKeys; 4 private Set<SelectionKey> publicSelectedKeys; 5 6 protected SelectorImpl(SelectorProvider var1) { 7 super(var1); 8 if (Util.atBugLevel("1.4")) { 9 this.publicKeys = this.keys; 10 this.publicSelectedKeys = this.selectedKeys; 11 } else { 12 this.publicKeys = Collections.unmodifiableSet(this.keys); 13 this.publicSelectedKeys = Util.ungrowableSet(this.selectedKeys); 14 } 15 16 }
SelectorImpl同樣呼叫父類AbstractSelector的構造:
1 protected AbstractSelector(SelectorProvider provider) { 2 this.provider = provider; 3 }
此時的provider就是剛才產生的WindowsSelectorProvider物件;
在SelectorImpl中還會對其成員有一系列的賦值操作;
上述都完成後才繼續完成WindowsSelectorImpl的構造。
WindowsSelectorImpl在進行this.wakeupSourceFd = ((SelChImpl)this.wakeupPipe.source()).getFDVal()之前,其wakeupPipe成員如下:
1 private final Pipe wakeupPipe = Pipe.open();
wakeupPipe管道通過Pipe.open()賦值:
1 public static Pipe open() throws IOException { 2 return SelectorProvider.provider().openPipe(); 3 }
可以看到實際上 SelectorProvider.provider()的provider的openPipe方法,而這個provider就是WindowsSelectorProvider,而WindowsSelectorProvider繼承自SelectorProviderImpl,openPipe方法是在SelectorProviderImpl裡實現的:
1 public Pipe openPipe() throws IOException { 2 return new PipeImpl(this); 3 }
該方法直接產生了PipeImpl物件,並將WindowsSelectorProvider物件傳入進去:
1 PipeImpl(SelectorProvider var1) throws IOException { 2 try { 3 AccessController.doPrivileged(new PipeImpl.Initializer(var1)); 4 } catch (PrivilegedActionException var3) { 5 throw (IOException)var3.getCause(); 6 } 7 }
可以看到這個構造方法實際上是以特權模式執行的PipeImpl的內部類Initializer的run方法(doPrivileged需要的引數是PrivilegedExceptionAction介面的實現類,該介面只有run方法):
Initializer 的初始化:
1 private class Initializer implements PrivilegedExceptionAction<Void> { 2 private final SelectorProvider sp; 3 private IOException ioe; 4 5 private Initializer(SelectorProvider var2) { 6 this.ioe = null; 7 this.sp = var2; 8 } 9 ...... 10 }
該構造方法給sp賦值為傳入進來的WindowsSelectorProvider物件,令ioe=null;
其所實現的run方法如下:
1 public Void run() throws IOException { 2 PipeImpl.Initializer.LoopbackConnector var1 = new PipeImpl.Initializer.LoopbackConnector(); 3 var1.run(); 4 if (this.ioe instanceof ClosedByInterruptException) { 5 this.ioe = null; 6 Thread var2 = new Thread(var1) { 7 public void interrupt() { 8 } 9 }; 10 var2.start(); 11 12 while(true) { 13 try { 14 var2.join(); 15 break; 16 } catch (InterruptedException var4) { 17 ; 18 } 19 } 20 21 Thread.currentThread().interrupt(); 22 } 23 24 if (this.ioe != null) { 25 throw new IOException("Unable to establish loopback connection", this.ioe); 26 } else { 27 return null; 28 } 29 }
首先產生LoopbackConnector 物件,是Initializer的內部類,而且實現了Runnable介面:
1 private class LoopbackConnector implements Runnable { 2 private LoopbackConnector() { 3 } 4 }
其實現的run方法如下:
1 public void run() { 2 ServerSocketChannel var1 = null; 3 SocketChannel var2 = null; 4 SocketChannel var3 = null; 5 6 try { 7 ByteBuffer var4 = ByteBuffer.allocate(16); 8 ByteBuffer var5 = ByteBuffer.allocate(16); 9 InetAddress var6 = InetAddress.getByName("127.0.0.1"); 10 11 assert var6.isLoopbackAddress(); 12 13 InetSocketAddress var7 = null; 14 15 while(true) { 16 if (var1 == null || !var1.isOpen()) { 17 var1 = ServerSocketChannel.open(); 18 var1.socket().bind(new InetSocketAddress(var6, 0)); 19 var7 = new InetSocketAddress(var6, var1.socket().getLocalPort()); 20 } 21 22 var2 = SocketChannel.open(var7); 23 PipeImpl.RANDOM_NUMBER_GENERATOR.nextBytes(var4.array()); 24 25 do { 26 var2.write(var4); 27 } while(var4.hasRemaining()); 28 29 var4.rewind(); 30 var3 = var1.accept(); 31 32 do { 33 var3.read(var5); 34 } while(var5.hasRemaining()); 35 36 var5.rewind(); 37 if (var5.equals(var4)) { 38 PipeImpl.this.source = new SourceChannelImpl(Initializer.this.sp, var2); 39 PipeImpl.this.sink = new SinkChannelImpl(Initializer.this.sp, var3); 40 break; 41 } 42 43 var3.close(); 44 var2.close(); 45 } 46 } catch (IOException var18) { 47 try { 48 if (var2 != null) { 49 var2.close(); 50 } 51 52 if (var3 != null) { 53 var3.close(); 54 } 55 } catch (IOException var17) { 56 ; 57 } 58 59 Initializer.this.ioe = var18; 60 } finally { 61 try { 62 if (var1 != null) { 63 var1.close(); 64 } 65 } catch (IOException var16) { 66 ; 67 } 68 69 } 70 71 }
在這個run方法中首先定義了三個Channel一個ServerSocketChannel和兩個SocketChannel,然後申請了兩個十六位元組的ByteBuffer緩衝區,定義了一個回送地址var6;在while迴圈中先檢查ServerSocketChannel是否開啟了,若沒有則需要呼叫open方法開啟並賦值給var1,繫結地址為var6即回送地址,埠為0,令var7這個InetSocketAddress物件的地址是var6,埠是ServerSocketChannel的埠;ServerSocketChannel初始化完畢,初始化一個SocketChannel即var2,通過剛才的var7這個InetSocketAddress物件和ServerSocketChannel建立連線;
在PipeImpl裡有一個靜態成員:
1 private static final Random RANDOM_NUMBER_GENERATOR = new SecureRandom();
RANDOM_NUMBER_GENERATOR 聽名字就知道它是用來生成隨機數;
通過RANDOM_NUMBER_GENERATOR將從生成的隨機數存放在其中一個緩衝區ByteBuffer(var4)中,然後通過剛才連線好的SocketChannel即var2的write方法寫入緩衝區中的所有可用資料傳送給ServerSocketChannel;令var4緩衝區標誌置0;接著ServerSocketChannel呼叫accept方法偵聽剛才的連線產生一個SocketChannel物件var3,從var3中讀取資料存放在緩衝區var5中,令var5緩衝區標誌置0;然後比較var4和var5中的內容是否一致,若是一致則給PipeImpl的成員source和sink分別初始化儲存起來,若不一致就繼續迴圈,不斷地重複上述過程,直至Pipe通道成功建立;至此結束LoopbackConnector的run方法。
其在連線建立的過程中若是出現了異常會通過Initializer的ioe成員儲存異常。
再回到Initializer的run方法,在完成LoopbackConnector的run方法後,再根據ioe判讀是否在剛才的連線建立中出現了ClosedByInterruptException異常,若是出現還需要通過執行緒啟動LoopbackConnector的run方法直至其結束;若不是ClosedByInterruptException異常則直接丟擲IOException。
至此PipeImpl的構造結束,再回到WindowsSelectorImpl的構造,通過上述的操作產生的PipeImpl物件就賦值給了wakeupPipe成員;wakeupPipe的source就是剛才產生的SourceChannelImpl物件,wakeupPipe的sink就是剛才產生的SinkChannelImpl物件,再使用wakeupSourceFd儲存source的fdVal值和wakeupSinkFd儲存sink的fdVal值;並且開啟Nagle演算法,最後使用pollWrpper成員儲存source的fdVal值。
Selector到此建立完畢。