Javanio的selector的register阻塞问题

今天上午打算实现一下将服务器的accept和read分开两个线程来处理,写了如下代码,结果发现程序阻塞在了accepthandler中的channel执行的register方法

package castest;

import javax.sound.midi.Soundbank;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class test2 {
    private Selector bossSelector;
    private Selector workerSelector;
    private ServerSocketChannel serverSocketChannel;
    private int PORT=8080;
//    public Object LOCK=new Object();
//    private ExecutorService acceptThreadPool;
//    private ExecutorService actionThreadPool;
    private Thread bossThread;
    private Thread workerThread;

    public test2(int port){
        super();
        PORT=port;
    }
    public test2(){
        super();
    }
    private void init(){
        try {
//            acceptThreadPool = Executors.newFixedThreadPool(2);
//            actionThreadPool = Executors.newFixedThreadPool(10);
            serverSocketChannel = ServerSocketChannel.open();
            bossSelector=Selector.open();
            workerSelector=Selector.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",PORT),60);
        } catch (IOException e) {
            System.err.println("server init failed one exception hapened message is "+e.getMessage());
        }
    }
    public void start(){
        init();
        try {
            serverSocketChannel.register(bossSelector, SelectionKey.OP_ACCEPT);
        } catch (ClosedChannelException e) {
            System.err.println("server start failed one exception hapened message is "+e.getMessage());
        }
        running();
        try {
            bossThread.join();
            workerThread.join();
            System.out.println("thread join finashed");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void runningServer() throws IOException {
        while (true){
            System.out.println("accepting");
            bossSelector.select();
            System.out.println("bossselect select finashed");
            Set<SelectionKey> selectionKeys = bossSelector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                iterator.remove();
                if (key.isAcceptable()) {
                    System.out.println("one connection inbound");
//                    acceptThreadPool.execute(new Thread(new ServerAcceptRunnable(key)));
                    ServerSocketChannel serverchannel = (ServerSocketChannel) key.channel();
                    System.out.println("tring to have one socketchannel");
                    SocketChannel channel = serverchannel.accept();
                    System.out.println("socketchannel acquery success");
                    channel.configureBlocking(false);
                    System.out.println("set no-blocking");
                    System.out.println(workerSelector==null);
                    System.out.println(channel.getClass());
//                    channel.register(bossSelector,SelectionKey.OP_READ);

                    channel.register(workerSelector,SelectionKey.OP_READ);
                    System.out.println("register socketchannel to workerselector");
                }//else if (key.isReadable()){
//                    System.out.println("reading");
//                    SocketChannel socketChannel = (SocketChannel) key.channel();
//                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//                    socketChannel.read(byteBuffer);
//                    System.out.println(new String(byteBuffer.array()));
//                    key.interestOps(SelectionKey.OP_WRITE);
//                }else if (key.isWritable()){
//                    System.out.println("writing");
//                    SocketChannel channel = (SocketChannel) key.channel();
//                    channel.write(ByteBuffer.wrap("test".getBytes()));
//                    key.cancel();
//                }
            }
        }
    }
    private void runningReadServer() throws IOException {
        while (true){
            System.out.println("working");
            int select = workerSelector.select();
                System.out.println("workerSSelector select");
                Set<SelectionKey> keys = workerSelector.selectedKeys();
                Iterator<SelectionKey> iterator = keys.iterator();
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    if (key.isReadable()){
                        System.out.println("one connection reading");
                        readAction(key);
                    }
            }
        }
    }
    private void readAction(SelectionKey key){
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        try {
            channel.read(buffer);
            String uri = new String(buffer.array());
            System.out.println(uri);
            channel.write(ByteBuffer.wrap("yes i receve your message".getBytes()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void running(){
        bossThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    runningServer();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        workerThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    runningReadServer();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        System.out.println("--------------");
        bossThread.start();
        System.out.println("bossThread start");
        workerThread.start();
        System.out.println("workerThread start");
    }
    public void shutdownServer(){
        try {
            bossThread.interrupt();
            workerThread.interrupt();
            bossSelector.close();
            workerSelector.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        test2 test2 = new test2();
        test2.start();
    }
}
//class ServerAcceptRunnable implements Runnable{
//    private SelectionKey key;
//    public ServerAcceptRunnable(SelectionKey key){
//        this.key=key;
//    }
//    @Override
//    public void run(){
//
//    }
//}

在加了断点,做了输出之后查了很久才发现,是由于selector是并发安全的所以channel的register方法会请求一个锁对象而另一个线程的selector的select方法也会请求这个方法并阻塞等待至少一个channel状态响应,所以就进入了死锁。
我想的解决办法就是将selector包装一下对他的大部分方法进行一个加锁和释放的操作并对select方法进行一个限制在不恰当的时候不执行select方法那么就可以使他不会与register抢夺锁,来防止底层代码执行而进入死锁。
修改后的代码如下:

package castest;

import javax.sound.midi.Soundbank;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class test2 {
    private selectorExtention bossSelector;
    private selectorExtention workerSelector;
    private ServerSocketChannel serverSocketChannel;
    private int PORT=8080;
//    public Object LOCK=new Object();
//    private ExecutorService acceptThreadPool;
//    private ExecutorService actionThreadPool;
    private Thread bossThread;
    private Thread workerThread;

    public test2(int port){
        super();
        PORT=port;
    }
    public test2(){
        super();
    }
    private void init(){
        try {
//            acceptThreadPool = Executors.newFixedThreadPool(2);
//            actionThreadPool = Executors.newFixedThreadPool(10);
            serverSocketChannel = ServerSocketChannel.open();
            bossSelector=selectorExtention.open();
            workerSelector=selectorExtention.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",PORT),60);
        } catch (IOException e) {
            System.err.println("server init failed one exception hapened message is "+e.getMessage());
        }
    }
    public void start(){
        init();
        try {
            bossSelector.reg(serverSocketChannel,SelectionKey.OP_ACCEPT);
        } catch (ClosedChannelException e) {
            System.err.println("server start failed one exception hapened message is "+e.getMessage());
        }
        running();
        try {
            bossThread.join();
            workerThread.join();
            System.out.println("thread join finashed");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void runningServer() throws IOException {
        while (true){
            System.out.println("accepting");
            bossSelector.select();
            System.out.println("bossselect select finashed");
            Iterator<SelectionKey> iterator = bossSelector.selectedkeys();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                iterator.remove();
                if (key.isAcceptable()) {
                    System.out.println("one connection inbound");
//                    acceptThreadPool.execute(new Thread(new ServerAcceptRunnable(key)));
                    ServerSocketChannel serverchannel = (ServerSocketChannel) key.channel();
                    System.out.println("tring to have one socketchannel");
                    SocketChannel channel = serverchannel.accept();
                    System.out.println("socketchannel acquery success");
                    channel.configureBlocking(false);
                    System.out.println("set no-blocking");
                    System.out.println(workerSelector==null);
                    System.out.println(channel.getClass());
//                    channel.register(bossSelector,SelectionKey.OP_READ);

                    workerSelector.reg(channel,SelectionKey.OP_READ);
                    System.out.println("register socketchannel to workerselector");
                }//else if (key.isReadable()){
//                    System.out.println("reading");
//                    SocketChannel socketChannel = (SocketChannel) key.channel();
//                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//                    socketChannel.read(byteBuffer);
//                    System.out.println(new String(byteBuffer.array()));
//                    key.interestOps(SelectionKey.OP_WRITE);
//                }else if (key.isWritable()){
//                    System.out.println("writing");
//                    SocketChannel channel = (SocketChannel) key.channel();
//                    channel.write(ByteBuffer.wrap("test".getBytes()));
//                    key.cancel();
//                }
            }
        }
    }
    private void runningReadServer() throws IOException {
        while (true){
            System.out.println("working");
            workerSelector.select();
                System.out.println("workerSSelector select");
                Iterator<SelectionKey> iterator = workerSelector.selectedkeys();
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    if (key.isReadable()){
                        System.out.println("one connection reading");
                        readAction(key);
                    }
            }
        }
    }
    private void readAction(SelectionKey key){
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        try {
            channel.read(buffer);
            String uri = new String(buffer.array());
            System.out.println(uri);
            channel.write(ByteBuffer.wrap("yes i receve your message".getBytes()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void running(){
        bossThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    runningServer();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        workerThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    runningReadServer();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        System.out.println("--------------");
        bossThread.start();
        System.out.println("bossThread start");
        workerThread.start();
        System.out.println("workerThread start");
    }
    public void shutdownServer(){
        try {
            bossThread.interrupt();
            workerThread.interrupt();
            bossSelector.selector().close();
            workerSelector.selector().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        test2 test2 = new test2();
        test2.start();
    }
}
//class ServerAcceptRunnable implements Runnable{
//    private SelectionKey key;
//    public ServerAcceptRunnable(SelectionKey key){
//        this.key=key;
//    }
//    @Override
//    public void run(){
//
//    }
//}
class selectorExtention{
    private Selector selector;
    boolean mark=false;
    private void init() throws IOException {
        selector=Selector.open();
    }
    public Iterator<SelectionKey> selectedkeys(){
        return selector.selectedKeys().iterator();
    }
    public static selectorExtention open(){
        selectorExtention selectorExtention = new selectorExtention();
        try {
            selectorExtention.init();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return selectorExtention;
    }
    public Selector selector(){return selector;}
    public synchronized void reg(SelectableChannel channel,int selectionKey) throws ClosedChannelException {
        channel.register(selector,selectionKey);
        mark=true;
    }
    public void select() throws IOException {
        if (mark){
            selector.select();
        }else {
            return;
        }
    }


}

上面这种修改的方式会使得worker线程不断的轮询,这样对性能是有消耗的,所以下面对selectorExtention类的方法进行一下修改做到如果没有channel就通过当前selector对象做锁阻塞select而不是selector的select方法的锁

class selectorExtention{
    private Selector selector;
    boolean mark=false;
//    private CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
//    CountDownLatch countDownLatch =new CountDownLatch(1);
    private void init() throws IOException {
        selector=Selector.open();
    }
    public Iterator<SelectionKey> selectedkeys(){
        return selector.selectedKeys().iterator();
    }
    public static selectorExtention open(){
        selectorExtention selectorExtention = new selectorExtention();
        try {
            selectorExtention.init();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return selectorExtention;
    }
    public Selector selector(){return selector;}
    public synchronized void reg(SelectableChannel channel,int selectionKey) throws ClosedChannelException {
        channel.register(selector,selectionKey);
        mark=true;
        this.notify();
//        cyclicBarrier.await();
    }
    public synchronized void select() throws IOException {
        if (!mark){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            selector.select();
        }else {
            selector.select();
//            cyclicBarrier.await();
            return;
        }
    }

你可能感兴趣的:(Java)