SelectorProvider.provider()

不同操作系统对 java.nio.channels.Selector 的实现存在显著差异,这主要源于它们底层 I/O 多路复用机制的不同。

我们以 Linux 系统为例。

Java NIO 程序中,通常会调用 SelectorProvider.provider().openSelector() 获取多路复用器 Selector

Linux 系统中,SelectorProvider 的静态方法 provider() 会返回 EPollSelectorProvider 对象。

package java.nio.channels.spi;
...
public abstract class SelectorProvider {
    ...
    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;
                        }
                    });
        }
    }
    ...
}

根据 provider() 方法的逻辑, 依次调用 3 个方法去获取 SelectorProvider 对象,直到首次获取成功后返回。

  • loadProviderFromProperty()
  • loadProviderAsService()
  • sun.nio.ch.DefaultSelectorProvider.create()

loadProviderFromProperty()

loadProviderFromProperty() 加载系统属性 -Djava.nio.channels.spi.SelectorProvider=XXX 指定的 SelectorProvider 实现类。这种方式优先级最高。

    private static boolean loadProviderFromProperty() {
        // 系统属性 "java.nio.channels.spi.SelectorProvider"
        String cn = System.getProperty("java.nio.channels.spi.SelectorProvider");
        if (cn == null)
            return false;
        try {
            // 加载 SelectorProvider 实现类,并初始化
            Class<?> c = Class.forName(cn, true,
                                       ClassLoader.getSystemClassLoader());
            // 创建 SelectorProvider 对象
            provider = (SelectorProvider)c.newInstance();
            return true;
        } catch (ClassNotFoundException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (IllegalAccessException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (InstantiationException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (SecurityException x) {
            throw new ServiceConfigurationError(null, x);
        }
    }

loadProviderAsService()

SelectorProvider 作为服务,由服务加载器 ServiceLoaderMETA-INF/services/java.nio.channels.spi.SelectorProvider 文件中加载 SelectorProvider 实现类。

private static boolean loadProviderAsService() {

    ServiceLoader<SelectorProvider> sl =
        ServiceLoader.load(SelectorProvider.class,
                           ClassLoader.getSystemClassLoader());
    Iterator<SelectorProvider> i = sl.iterator();
    for (;;) {
        try {
            if (!i.hasNext())
                return false;
            provider = i.next();
            return true;
        } catch (ServiceConfigurationError sce) {
            if (sce.getCause() instanceof SecurityException) {
                // Ignore the security exception, try the next provider
                continue;
            }
            throw sce;
        }
    }
}

ServiceLoader.load() 会创建一个 LazyIterator 懒加载迭代器对象。顾名思义,只有在迭代时才会到 META-INF/services/java.nio.channels.spi.SelectorProvider 中查找并加载 SelectorProvider 实现类。

i.hasNext()

i.hasNext() 会委托 LazyIterator 对象去 META-INF/services/java.nio.channels.spi.SelectorProvider 中查找 SelectorProvider 实现类。

public boolean hasNext() {
    if (knownProviders.hasNext())
        return true;
    return lookupIterator.hasNext();
}

knownProviders 被用作缓存,加快查找速度。如果缓存中有,直接使用缓存中的数据。

lookupIterator 字段保存的就是 LazyIterator 对象。

lookupIterator.hasNext() 代码如下:

public boolean hasNext() {
    if (acc == null) {
        return hasNextService();
    } else {
        PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
            public Boolean run() { return hasNextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}

实际调用 hasNextService() 完成处理.

private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        try {
            String fullName = PREFIX + service.getName();
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    ....
}

PREFIX 字段保存字符串 "META-INF/services/"。而 service 字段保存的是 java.nio.channels.spi.SelectorProvider 的 Class 对象。因此 fullName 就是 META-INF/services/java.nio.channels.spi.SelectorProviderfullName 文件中保存了 SelectorProvider 实现类的全限定名称。将实现类全限定名字符串加载到内存中。

i.next()

知道了类的全限定名就可以加载该类了。这是在通过 i.next()完成的。

public S next() {
    if (knownProviders.hasNext())
        return knownProviders.next().getValue();
    return lookupIterator.next();
}

i.next() 会委托给 LazyIterator 对象的 lookupIterator.next() 方法加载类。

public S next() {
    if (acc == null) {
        return nextService();
    } else {
        PrivilegedAction<S> action = new PrivilegedAction<S>() {
            public S run() { return nextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}

next() 方法调用 nextService() 完成主要逻辑。

private S nextService() {
    ....
    try {
        c = Class.forName(cn, false, loader);
    } catch (ClassNotFoundException x) {
        ...
    }
}

sun.nio.ch.DefaultSelectorProvider.create()

我来看看 Linux 对 sun.nio.ch.DefaultSelectorProvider.create() 的具体实现。

public static SelectorProvider create() {
    String osname = AccessController
        .doPrivileged(new GetPropertyAction("os.name"));
    if (osname.equals("SunOS"))
        return createProvider("sun.nio.ch.DevPollSelectorProvider");
    if (osname.equals("Linux"))
        return createProvider("sun.nio.ch.EPollSelectorProvider");
    return new sun.nio.ch.PollSelectorProvider();
}

直接硬编码类名 "sun.nio.ch.EPollSelectorProvider"。可见,这种方式是兜底的。只有前面两种动态加载的方式都不起作用时,才会调用该方法。

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();
    }
}

sun.nio.ch.EPollSelectorProvider 继承自 sun.nio.ch.SelectorProviderImpl,并重写了 openSelector() 方法。

EPollSelectorProvider 有 3 个主要方法:

  • openSelector():在 EPollSelectorProvider 中进行了重写。返回 EPollSelectorImpl 对象。

    public AbstractSelector openSelector() throws IOException {
        return new EPollSelectorImpl(this);
    }
    
  • openServerSocketChannel():在父类 SelectorProviderImpl 中实现。服务端使用该方法获得一个 ServerSocketChannel 对象。

    public ServerSocketChannel openServerSocketChannel() throws IOException {
        return new ServerSocketChannelImpl(this);
    }
    
  • openSocketChannel():在父类 SelectorProviderImpl 中实现。客户端使用该方法获得一个 SocketChannel 对象

    public SocketChannel openSocketChannel() throws IOException {
        return new SocketChannelImpl(this);
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值