NIO is reasonable for performance, and should use no more than #cores threads, multiplicated by the IO factor of your application, where the IO factor is the percentage of your app waiting for disk IO to complete.
The reason is simple. When you have #cores worker, each worker is likely to be bound to a single cpu core and can unitilize it to its maximum. the more workers, the more context switches, and this is exactly what you don't want and why you use NIO in the first place.
If the workers have to wait for IO, they could handle other requests, so use some more workers than cores for full cpu utilization.
If you use threads, you get the following advantages:
you can store session information in ThreadLocals.
you don't have to manage the session information in other ways.
You can use the simple InputStream/OutputStream (or Reader/Writer) API, and wrapping Streams around each other. In my current project, I'm using a stack of
MessageFormatter, a custom formatting class
PrintWriter
OutputStreamWriter
DebugOutputStream (custom class, which makes a copy for debugging purposes)
DeflatorOutputStream (a custom subclass, in fact, which supports flushing)
the OutputStream of a SSLSocket
and the other way around on the receiving side. This is quite comfortable, since you only have to deal with the top layer in your program logic (the rest is mostly one constructor call each).
you need a new Thread (or even pair of threads, depending on architecture) for each connection.
(In my project I have a MessageParser-Thread for each connection, which then gives individual jobs to a ThreadPool, and these jobs then may write to one or several of the open connections, not only the one which spawned them. The writing is synchronized, of course.)
each thread needs quite a bit of stack space, which can be problematic if you are on a resource limited machine.
In the case of short-lived connections, you actually don't want a new thread for each connection, but only a new Runnable executed on a ThreadPool, since construction of new Threads takes a bit of time.
nonblocking IO
if you have such a multi-layer architecture with multiple conversions, you have to arrange it all by yourself. It is possible:
my MessageFormatter can write to a CharBuffer as well as to a Writer.
for the Char-to-Byte-formatting, use CharsetEncoder/CharsetDecoder (transfers data between CharBuffer and ByteBuffer).
for the compressing, I used wrapper classes around Deflater/Inflater, which transfers data between two ByteBuffers.
for the encryption, I used a SSLEngine (one for each connection), which also uses ByteBuffers for input and output.
Then, write to a SocketChannel (or in the other direction, read from it).
Still, the management overhead is quite a hassle, as you must track where the data is. (I actually had a two pipelines for each connection, and one or two threads managing the data in all the pipelines, between this waiting on a Selector for new data on the sockets (or for new space there, in the outgoing case). (The actual processing after the parsing of the Messages still took place in spawned Runnables in a ThreadPool.)
You need only a small number of threads. This was actually the reason I tried to do this asynchronously. It worked (with the same synchronous client), but was way slower than my multi-thread solution, so I put this back until we run out of memory for too much threads. (Until now, there are not so much connections at the same time.)
There's nothing wrong with either approach. If you have limited clients the second option will suffice (and possibly even thrive based on a multicore architecture), otherwise it might be beneficial to let java.nio manage your resources.
See this question on the same topic, as well as this other post, or why not consider this blog post which argues against using java.nio for most scenarios.
NIO for the win and I can actually stop threads when I want to. Learning NIO is nowehere difficult, however using buffers properly is almost never explained anywhere. One of the reasons, I believe, people can't squeeze NIO for exrta benefits.
Other parts of NIO is general inability of the most developers to code and use state machines, so they end up copying the buffers way too much.
I tried Apache MINA, it is really really good. I strongly recommend it.
NIO is reasonable for performance, and should use no more than #cores threads, multiplicated by the IO factor of your application, where the IO factor is the percentage of your app waiting for disk IO to complete.
The reason is simple. When you have #cores worker, each worker is likely to be bound to a single cpu core and can unitilize it to its maximum. the more workers, the more context switches, and this is exactly what you don't want and why you use NIO in the first place.
If the workers have to wait for IO, they could handle other requests, so use some more workers than cores for full cpu utilization.
If you use threads, you get the following advantages:
Individual threads
You can use the simple InputStream/OutputStream (or Reader/Writer) API, and wrapping Streams around each other. In my current project, I'm using a stack of
and the other way around on the receiving side. This is quite comfortable, since you only have to deal with the top layer in your program logic (the rest is mostly one constructor call each).
you need a new Thread (or even pair of threads, depending on architecture) for each connection.
(In my project I have a MessageParser-Thread for each connection, which then gives individual jobs to a ThreadPool, and these jobs then may write to one or several of the open connections, not only the one which spawned them. The writing is synchronized, of course.)
each thread needs quite a bit of stack space, which can be problematic if you are on a resource limited machine.
In the case of short-lived connections, you actually don't want a new thread for each connection, but only a new Runnable executed on a ThreadPool, since construction of new Threads takes a bit of time.
nonblocking IO
if you have such a multi-layer architecture with multiple conversions, you have to arrange it all by yourself. It is possible:
Still, the management overhead is quite a hassle, as you must track where the data is. (I actually had a two pipelines for each connection, and one or two threads managing the data in all the pipelines, between this waiting on a Selector for new data on the sockets (or for new space there, in the outgoing case). (The actual processing after the parsing of the Messages still took place in spawned Runnables in a ThreadPool.)
You need only a small number of threads. This was actually the reason I tried to do this asynchronously. It worked (with the same synchronous client), but was way slower than my multi-thread solution, so I put this back until we run out of memory for too much threads. (Until now, there are not so much connections at the same time.)
There's nothing wrong with either approach. If you have limited clients the second option will suffice (and possibly even thrive based on a multicore architecture), otherwise it might be beneficial to let java.nio manage your resources.
See this question on the same topic, as well as this other post, or why not consider this blog post which argues against using java.nio for most scenarios.
NIO for the win and I can actually stop threads when I want to. Learning NIO is nowehere difficult, however using buffers properly is almost never explained anywhere. One of the reasons, I believe, people can't squeeze NIO for exrta benefits.
Other parts of NIO is general inability of the most developers to code and use state machines, so they end up copying the buffers way too much.