java 服务器阻塞_Java服务器非阻塞笔记

Java在做服务器的时候,我们司空见惯的就是阻塞式的方式,也就是每当发生一次连接,就new 一个Thread出来,假如线程在读写,连接发生问题,线程则会一直阻塞,但是并不会消亡。所以随着线程数的增加,CPU的利用率会随之降低,因此我们应当采用非阻塞式的方式,能更好的解决问题。

看了一本书《Java网络编程精解》最近才大致的通读了一遍,这本书上面讲解的很详细,简单的将前面的非阻塞与阻塞的结合部分,代码做了一定的修改。并自己进行了一些抽象,测试通过没有提。

用一个AcceptThread采用阻塞的方式,来处理连接。

用一个RWThread采用非阻塞的方式,来处理读写问题。

这样既充分利用了多核心CPU的特性,两个线程做两个事情,也解决了开始提到的问题。

public classApplication {//存在死锁风险,再认真考虑一下,虽然概率低一些。读写和接收线程问题!!!!

public static voidmain(String[] args){

Server server=Server.getInstance();//启动接收线程

newAcceptThread(server).start();//启动读写线程

newRWThread(server).start();

}

}这个是我们这个程序的入口。主方法启动两个线程

importjava.io.IOException;importjava.net.InetSocketAddress;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;//使用单例模式,创建唯一的Selector

public classServer {private staticServer server;//选择器

publicSelector selector;//创建serverSocket

publicServerSocketChannel serverSocket;//端口号

private int port = 8086;privateServer(){try{//初始化Selector

selector =Selector.open();//初始化serverSocket

serverSocket =ServerSocketChannel.open();

serverSocket.configureBlocking(false);//可以绑定到相同端口

serverSocket.socket().setReuseAddress(true);//绑定到某个地址上

serverSocket.socket().bind(newInetSocketAddress(port));

serverSocket.register(selector, SelectionKey.OP_ACCEPT);

}catch(IOException e) {

e.printStackTrace();

}

}public staticServer getInstance(){if(server == null){

server= newServer();

}returnserver;

}

}

我们使用单例模式,创建ServerSocketChannel,获取唯一的Selector,因为Selector当中会将ServerSocketChannel,SocketChannel的key存储在Selector当中。(注:正因为两个线程分别一个接受要往Selector当中添加,另一个读写线程要从Selector当中获取SocketChannel,所以要防止死锁情况的出现)与此同时,我们还给ServerSocket注册了Accept事件

importjava.io.IOException;importjava.nio.ByteBuffer;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.util.Iterator;public class AcceptThread extendsThread{privateSelector selector;publicAcceptThread(Server server){

selector=server.selector;

}public voidrun() {try{while(true){

selector.select();//阻塞

Iterator it =selector.selectedKeys().iterator();while(it.hasNext()){

SelectionKey keys=it.next();//接收状态下的操作

if(keys.isAcceptable()){//获取当前链接的SocketChannel

ServerSocketChannel ssc =(ServerSocketChannel)keys.channel();

SocketChannel sc=ssc.accept();if(sc!=null){//非阻塞

sc.configureBlocking(false);synchronized(selector){

System.out.println("连接成功");

sc.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);

}

}

}

}

}

}catch(IOException e) {

e.printStackTrace();

}

}

}

我们通过便利Selector对象来获取Accept事件状态,从而获取SocketChannel,并且注册Write/Read事件我们会将SocketChannel注册到Selector当中,因此在注册事件的同时,我们要加锁。

刚开始,我们采用的是select()方法,这个方法是阻塞的,如果不发生连接此方法将一直阻塞下去。

importjava.io.IOException;importjava.nio.ByteBuffer;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.util.Iterator;public class RWThread extendsThread {//key集合类

privateSelector selector;//BufferByte

privateByteBuffer byteBuffer;publicRWThread(Server server){

selector=server.selector;

}

@Overridepublic voidrun() {while(true){synchronized(selector){try{if(selector.selectNow()>0){ //非阻塞

System.out.println(selector.selectedKeys().size());

Iterator it =selector.selectedKeys().iterator();while(it.hasNext()){

SelectionKey keys=it.next();//可读状态下的操作

if(keys.isReadable()){

}//可写状态下的操作

if(keys.isWritable()){

}try{

Thread.sleep(10);

}catch(InterruptedException e) {//TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}catch(IOException e) {

e.printStackTrace();

}

}

}

}

}

这次读写selectNow()是非阻塞的,所以将会一直执行下去,我们为了不让CPU运行太高,因此加入了一个睡眠线程,从而降低CPU的使用。

后来想到的:

这样的效率并不是最好的,因为我们每一次it.next(),我们都会讲Selector当中注册时事件全部遍历下来,从而才会获取到相应的事件,并作出处理。因此将读写的Selector与接收的Accept事件分开,可能会好一些。也就是Accept当中注册读写事件的时候,将事件注册到一个新的Selector当中。单独遍历不同的Selector,这样也避免了死锁的存在问题才是。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值