Issue
I am currently developing my event driven Java software in the following fashion (this is the essence of my main method):
while(true) {
Event event = eventListener.poll();
if(event != null) {
// do something
} else {
// do nothing as usual, but burn CPU time.
}
}
Depending on what I'm building, eventListener
could be something that's listening to an external websocket, polling a Redis channel for updates, or waiting for a message from another process sitting on the same box (perhaps through UDP/TCP/shm).
My thinking is that this busy loop approach wastes a lot of CPU time when eventListener
is returning null (which is most of the time), since it just sits there spinning. However, I don't know how else to approach this design, aside from putting a Thread.sleep
each iteration which is not a great solution.
Ideally I would like to have a method:
void run(Event event) {
// do something
}
where run
is called any time an event hits eventListener
. If no such event is available, the process should ideally just sitting there idling.
Now, I know there's websocket libraries that can actually do this, what I want to know is how can I build something like this for myself and liberate my CPU from sitting there wasting itself doing nothing?
Solution
You need to use java non-blocking IO and may be some library that supports high level communication via java NIO (for example netty which supports NIO style communication for HTTP, websockets and redis among many others).
Here's a short description how NIO works. The thing you are looking is Selector
. It allows to wait until data on a Channel (which is an abstraction for a file or a network connection etc) is available. This wait (Selector.select
method) is blocking and the process is resumed by OS when some data is available to be read or the output buffer to write to can get new data.
Schematically the code looks like this:
Selector selector = createSelector();
Channel channel = createChannelForSocket();
SelectionKey key = channel.register(selector);
while(true) {
int readyChannels = selector.select(TIMEOUT);
if(readyChannels == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
for(SelectionKey key : selectedKeys) {
if (key.isReadable()) {
readDataFromChannel(key.channel())
} else if (key.isWritable()) {
writeDataToChannel(key.channel())
}
}
}
With netty you have more high level code where you define a Handler
which has a method like void channelRead(ChannelHandlerContext ctx, Object msg)
which is kind of read event listener that you can implement to listen to read events.
netty has a built-in loop that looks similar to the above example but for many event listeners and it propagates those events to particular listeners.
Answered By - Roman-Stop RU aggression in UA
Answer Checked By - Dawn Plyler (JavaFixing Volunteer)