多线程改进Selector

前面的代码在服务器端都是单线程配合一个 Selector 选择器来管理多个 channel 上的事件

这样有两个缺点

  1. 现在都是多核CPU,一个线程只能用一个核心,其他的核心会白白浪费
  2. 单线程是可以处理多个事件,但是如果某个事件耗时较长,就会造成其他事件的等待

如果让你设计一种较为合理的架构,你会怎样设计呢?

首先一点要把多核CPU 充分利用起来,第二就是每个线程对应自己的职责,例如,店小二负责接待,厨师负责炒菜,服务员负责记录菜单

代码实现

服务器

package com.zhao.c1;

import com.zhao.io.ByteBufferUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * @Auther: HackerZhao
 * @Date: 2021/11/2 12:05
 * @Description: 多线程管理多个 channel
 */

@Slf4j
public class MultiThreadServer {

    public static void main(String[] args) throws IOException {
        // 设置main 名称为 Boss
        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));

        // 1. 创建固定数量的 work 并初始化
        Work work = new Work("work0");
        work.register();
        while (true) {

            // 判断是否有事件发生,如果四种事件其中一种发生了,会向下运行。没有发生,则阻塞
            boss.select();
            Iterator<SelectionKey> iter = boss.selectedKeys().iterator();
            while (iter.hasNext()) {
                SelectionKey key = iter.next();
                iter.remove();
                if (key.isAcceptable()) {
                    // accept 建立与客户端连接,SocketChannel 用来与客户端之间通信(菜单)
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false);
                    log.debug("connected...{}",sc.getRemoteAddress());
                    // 如果 work 创建在连接里边,那么每创建一个连接就会创建一个work,太浪费资源了(work应该是独立的,它只负责自己的读写事件)
                    // 2. 关联 selector,此时就不能关联 boss 这个 selector 了,应当关联对象 work 中的 selector
                    log.debug("before register...{}",sc.getRemoteAddress());
                    sc.register(work.selector,SelectionKey.OP_READ,null);
                    log.debug("after register...{}",sc.getRemoteAddress());
                }

            }
        }
    }

    static class Work implements Runnable {
        private Thread thread;   // 独立的线程
        private Selector selector;  // 独立的监听器
        private String name; // work  的名字

        private volatile boolean start; // 刚开始线程还未初始化

        public Work(String name) {
            this.name = name;
        }

        // 初始化 Thread 和 Selector
        public void register() throws IOException {
            if (!start) {
                thread = new Thread(this, name);  // 创建一个新线程,就是当前类
                thread.start();
                selector = Selector.open();
                start = true;
            }
        }

        @Override
        public void run() {
            while (true){
                try {
                    selector.select();
                    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();
                            // 读取到了 channel 中的数据
                            log.debug("read...{}",channel.getRemoteAddress());
                            channel.read(buffer);
                            // 切换至读模式
                            buffer.flip();
                            ByteBufferUtil.debugAll(buffer);
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客户端

package com.zhao.c1;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

/**
 * @Auther: HackerZhao
 * @Date: 2021/11/2 13:17
 * @Description: 客户端
 */
public class TestClient {

    public static void main(String[] args) throws IOException {
        SocketChannel sc = SocketCha
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海上钢琴师_1900

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值