Nio-selector之多线程
场景说明:
略
服务端代码
@Slf4j
public class MultiThreadServer {
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(9080));
//创建国定数量
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> iter = boss.selectedKeys().iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
if (key.isAcceptable()) {
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
log.debug("建立了连接以后的输出:{}",sc.getRemoteAddress());
//2.关联
log.debug("关联之前:{}",sc.getRemoteAddress());
//负载均衡
workers[index.getAndIncrement() % workers.length].register(sc); //被boss线程调用
log.debug("关联之后:{}",sc.getRemoteAddress());
}
}
}
}
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;
}
//初始化线程和selector
public void register(SocketChannel sc) throws IOException {
if (!start) {
thread = new Thread(this,name);
thread.start();
selector = Selector.open();
start = true;
}
//向队列中添加任务,但这个任务并没有立刻执行
queue.add(() -> {
try {
sc.register(selector,SelectionKey.OP_READ,null);
} catch (ClosedChannelException e) {
throw new RuntimeException(e);
}
});
selector.wakeup(); //唤醒 selector
}
@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> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
if (key.isReadable()) {
ByteBuffer buffer = ByteBuffer.allocate(16);
SocketChannel channel = (SocketChannel) key.channel();
log.debug("读到的数据:{}",channel.getRemoteAddress());
channel.read(buffer);
buffer.flip();
debugAll(buffer);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
为了让两个线程之间能相互通信,这里采用队列的方式
客户端
public class TestClient {
public static void main(String[] args) throws IOException {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("127.0.0.1",9080));
sc.write(Charset.defaultCharset().encode("1234567890abcdef"));
System.in.read();
}
}