前言
NIO是一个Selector同时轮询多个Channel,监听读写事件。我们可以使用多线程来处理。多个Selector线程,多个处理读写的线程。
让boss线程专门处理accept事件。
worker线程来处理读事件。但是这里有一个问题,boss线程会阻塞worker0线程的执行,所以这里需要专门处理。
一、使用队列共享资源
- 目的是:让boss线程在worker线程中执行
@Slf4j
public class MultiThreadServer {
static class Worker implements Runnable {
private Thread thread;
private Selector selector;
private String name;
private ConcurrentLinkedQueue<Runnable> queue = new ConcurrentLinkedQueue<>();
private volatile boolean start = false;
public Worker(String name) {
this.name = name;
}
public void register(SocketChannel sc) throws IOException {
if (!start) {
selector = Selector.open();
thread = new Thread(this, name);
thread.start();
start = true;
}
queue.add(() -> {
try {
sc.register(selector, SelectionKey.OP_READ, null);
} catch (ClosedChannelException e) {
e.printStackTrace();
}
});
//唤醒select
selector.wakeup();
}
@Override
public void run() {
while (true) {
try {
selector.select();
Runnable task = queue.poll();
if (task != null) {
task.run();//这个位置执行了 sc.register(selector,SelectionKey.OP_READ,null);
}
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isReadable()) {
ByteBuffer buffer = ByteBuffer.allocate(16);
SocketChannel channel = (SocketChannel) key.channel();
log.debug("read...{}", channel.getRemoteAddress());
channel.read(buffer);
buffer.flip();
debugAll(buffer);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws IOException {
Thread.currentThread().setName("boss");
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
Selector boss = Selector.open();
SelectionKey bossKey = ssc.register(boss, 0, null);
bossKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
Worker worker = new Worker("worker-0");
while (true) {
boss.select();
Iterator<SelectionKey> iterator = boss.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
log.debug("connected...{}", sc.getRemoteAddress());
log.debug("before register...{}", sc.getRemoteAddress());
//关联selector
worker.register(sc);
log.debug("after register...{}", sc.getRemoteAddress());
}
}
}
}
}
二、模拟多worker
public static void main(String[] args) throws IOException {
Thread.currentThread().setName("boss");
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
Selector boss = Selector.open();
SelectionKey bossKey = ssc.register(boss, 0, null);
bossKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
Worker[] workers=new Worker[Runtime.getRuntime().availableProcessors()];
for (int i=0;i<workers.length;i++){
workers[i]=new Worker("worker-"+i);
}
AtomicInteger index=new AtomicInteger();
while (true){
boss.select();
Iterator<SelectionKey> iterator = boss.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if(key.isAcceptable()){
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
log.debug("connected...{}",sc.getRemoteAddress());
log.debug("before register...{}",sc.getRemoteAddress());
//关联selector round robin 轮询workers
workers[index.getAndIncrement()%workers.length].register(sc);
log.debug("after register...{}",sc.getRemoteAddress());
}
}
}
}