请问Java网络编程如何在不使用多线程的情况下实现异步返回?

07-09-29 SSnake
         

我指的是在不使用多线程的情况下进行并发处理

具体的情况是,在不使用多线程的情况下,服务器侦听某个端口,在有连接进来的时候会调用某个函数对此连接进行处理,但是由于处理的过程可能会比较长,为了不让后面连接的用户等待,需要此函数能异步返回,而不是阻塞在这个函数。

之所以希望不使用多线程,是因为考虑到同时连接的用户数会比较多,如果用多线程的话,线程创建,销毁和切换的开销会太大。虽然可以使用线程池,但是这样只是省去了创建和销毁线程的开销,线程切换的开销还是会存在,而且线程的数目也不好确定。

.NET中的Socket类有个方法是BeginReceive,这个方法中可以定义一个回调函数,在调用BeginReceive之后,回调函数会被调用,而BeginReceive可以立即返回,因此在不使用多线程的情况下可以实现并发处理。

我要的就是这种效果,请问在Java中怎么实现?

我找了些资料,里面提到用ServerSocketChannel,即NIO可以实现非阻塞,但是我实验了一下,好像不行。

代码如下:

package snake.test.channel;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.util.Iterator;

import java.util.Set;

class Server {

public static void main(String[] args) {

Reactor re = null;

try {

re = new Reactor(6656);

re.run();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

class Reactor implements Runnable {

final Selector selector;

final ServerSocketChannel serverSocket;

public int num = 0;

Reactor(int port) throws IOException {

selector = Selector.open();

serverSocket = ServerSocketChannel.open();

serverSocket.socket().bind(new InetSocketAddress(port));

serverSocket.configureBlocking(false);

SelectionKey sk = serverSocket.register(selector,

SelectionKey.OP_ACCEPT);

sk.attach(new Acceptor());

}

public void run() { // normally in a new Thread

try {

while (!Thread.interrupted()) {

selector.select();

Set selected = selector.selectedKeys();

Iterator it = selected.iterator();

while (it.hasNext()) {

dispatch((SelectionKey) (it.next()));

}

selected.clear();

}

} catch (IOException ex) { /* ... */

}

}

void dispatch(SelectionKey k) {

Runnable r = (Runnable) (k.attachment());

if (r != null)

r.run();

}

class Acceptor implements Runnable { // inner

public void run() {

try {

num++;

SocketChannel c = serverSocket.accept();

if (c != null)

new Handler(selector, c, num);

} catch (IOException ex) { /* ... */

}

}

}

}

////////////////////////////////////////////

package snake.test.channel;

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

final class Handler implements Runnable {

final SocketChannel socket;

final SelectionKey sk;

ByteBuffer input = ByteBuffer.allocate(1024);

ByteBuffer output = ByteBuffer.allocate(1024);

static final int READING = 0, SENDING = 1;

int state = READING;

private int num = 0;

Handler(Selector sel, SocketChannel c,int n) throws IOException {

System.out.println("Client Startup");

socket = c;

num = n;

c.configureBlocking(false);

// Optionally try first read now

sk = socket.register(sel, 0);

sk.attach(this);

sk.interestOps(SelectionKey.OP_READ);

sel.wakeup();

}

boolean inputIsComplete() {

return true; /* ... */

}

boolean outputIsComplete() {

return true; /* ... */

}

void process() {

//System.out.println("Sleep");

try {

Thread.sleep(100000000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

// class Handler continued

public void run() {

try {

System.out.println("Run " + num);

if (state == READING)

read();

else if (state == SENDING)

send();

} catch (IOException ex) { /* ... */

}

}

void read() throws IOException {

socket.read(input);

if (inputIsComplete()) {

process();

state = SENDING;

// Normally also do first write now

sk.interestOps(SelectionKey.OP_WRITE);

}

}

void send() throws IOException {

socket.write(output);

if (outputIsComplete())

sk.cancel();

}

}

请问是否少了什么东西?

或者有什么别的技术?还是说一定使用多线程技术?

谢谢!

         

SSnake
2007-09-29 17:27

补充一下,Handler类中的process函数中调用了Thread.Sleep让程序睡眠一段长时间,如果可以异步返回的话,则多个用户可以并发处理,但是不成功

麻烦各位帮忙看看谢谢

coolzyt
2007-09-30 15:12

NIO是不阻塞的等待一个连接,而是采用了监听的方式来实现,到后面,你需要read,write的时候,如果所需的条件没有准备好,其实一样要阻塞的啊。

如果不想让用户等待,肯定至少用户调用的方法,和执行昂贵的操作的方法,肯定不能是一个线程了,但是不一定要每次都开一个线程,我们可以重复的利用一个线程来不停执行新的请求所需要的昂贵操作。

这里可以使用生产者消费者模式,你可以顺便研究一下1.5加入的concurrent包,肯定可以满足你的要求