线程池工作原理:
创建线程池的时候一开始会初始化好几个线程,会给每一个线程分配一个工作队列。然后从任务队列中取出一个任务,如果有任务,执行线程的run方法,如果没有任务,则进行阻塞队列,直到工作队列有新的任务进来。才继续取出一个任务。
如果线程池中有多个工作队列就是如下所示(下图假设有 这个线程池有三个工作队列),会有顺序分配到三个队列,这三个队列是并发的执行。
结论:
一个Thread + 一个队列 == 一个单线程的线程池 =====> 线程安全。任务时线性串行执行的 (SingleThreadEventExecutor)
线程安全,不会产生阻塞效应,使用对象组
线程不安全,会产生阻塞效应,使用对象池
单客户端多连接的时候,要怎么去维护多个连接?(维护多个缓存对象)
两种方案:
(1)对象池
1、首先初始化N个对象。
2、外部需要获取一个对象时,会从队列出栈一个对象,且将对象从栈中移除
3、使用对象
4、使用完之后释放资源
没有可用对象会存在两种情况:(1)创建对象 (2)阻塞等待 使用完会归还对象池中。如果超出有另外的情况
数据库连接池就是使用这种方法。
(2)对象组
1、初始化N个对象,用数组进行缓存。
2、外部需要使用的话,就从数组中获取一个对象
3、外部使用对象
4、无需归还
代码实现单客户端多连接:
客户端启动类:
package com.client;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* 单客户端多连接的 (一个 客户端有多个channel)
* 这样服务端有多个连接进来
* 启动类
*
*/
public class Start {
public static void main(String[] args) {
MultClient client = new MultClient();
client.init(5);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
while(true){
try {
System.out.println("请输入:");
String msg = bufferedReader.readLine();
// 通过客户端获取一个连接
client.nextChannel().writeAndFlush(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 当我们断开网开,再连接的话就会重连
*/
}
}
多客户端类:
package com.client;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* 多连接客户端
*/
public class MultClient {
/**
* 服务类
*/
private Bootstrap bootstrap = new Bootstrap();
/**
* 会话
*/
private List<Channel> channels = new ArrayList<>();
/**
* 引用计数 原子递增类
*/
private final AtomicInteger index = new AtomicInteger();
/**
* 初始化
* @param count
*/
public void init(int count){
//worker
EventLoopGroup worker = new NioEventLoopGroup();
//设置线程池
bootstrap.group(worker);
//设置socket工厂、
bootstrap.channel(NioSocketChannel.class);
//设置管道
bootstrap.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new ClientHandler());
}
});
for(int i=1; i<=count; i++){
// 192.168.0.103 是本机的IP
ChannelFuture future = bootstrap.connect("192.168.0.103", 10101);
channels.add(future.channel());
}
}
/**
* 获取会话 (对外提供一个方法获取会话)
*/
public Channel nextChannel(){
// 获取一个可用的会话
return getFirstActiveChannel(0);
}
private Channel getFirstActiveChannel(int count){
//AtomicInteger index 是原子类递增,可以实现很均匀的分配
Channel channel = channels.get(Math.abs(index.getAndIncrement() % channels.size()));
// 连接有可能是断开,加入已经断开连接了,我们需要进行尝试重连
if(!channel.isActive()){
//重连
reconnect(channel);
if(count >= channels.size()){
throw new RuntimeException("no can use channel");
}
return getFirstActiveChannel(count + 1);
}
return channel;
}
/**
* 重连
* @param channel
*/
private void reconnect(Channel channel){
synchronized(channel){
// 已经不在这个数组的时候,已经重连过了
if(channels.indexOf(channel) == -1){
return ;
}
Channel newChannel = bootstrap.connect("192.168.0.103", 10101).channel();
channels.set(channels.indexOf(channel), newChannel);
}
}
}
服务端:
package com.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* netty5服务端
*/
public class Server {
public static void main(String[] args) {
//服务类
ServerBootstrap bootstrap = new ServerBootstrap();
//boss和worker 事件循环体
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
//设置线程池
bootstrap.group(boss, worker);
//设置socket工厂
bootstrap.channel(NioServerSocketChannel.class);
//设置管道工厂
bootstrap.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new ServerHandler());
}
});
//netty3中对应设置如下
//bootstrap.setOption("backlog", 1024);
//bootstrap.setOption("tcpNoDelay", true);
//bootstrap.setOption("keepAlive", true);
//设置参数,TCP参数
bootstrap.option(ChannelOption.SO_BACKLOG, 2048);//serverSocketchannel的设置,链接缓冲池的大小
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);//socketchannel的设置,维持链接的活跃,清除死链接
bootstrap.childOption(ChannelOption.TCP_NODELAY, true);//socketchannel的设置,关闭延迟发送
//绑定端口
ChannelFuture future = bootstrap.bind(10101);
System.out.println("start");
//等待服务端关闭
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally{
//释放资源
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
服务端类:
package com.server;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 服务端消息处理
*/
public class ServerHandler extends SimpleChannelInboundHandler<String> {
// 这里的String可以传入一个泛型参数 String -> String msg
@Override
protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg);
ctx.channel().writeAndFlush("hi");
ctx.writeAndFlush("hi");
}
/**
* 新客户端接入
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
/**
* 客户端断开
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelInactive");
}
/**
* 异常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
}
}
netty源码解析比较好的