今天上午打算实现一下将服务器的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;
}
}