简体   繁体   中英

How to avoid a busy while loop in event-driven Java

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?

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.

If you're interested in using event-driven architecture at scale. You might want to use a robust "event bus", like Apache Kafka or AWS SNS+SQS. To make it easier, you can use kalium.alkal.io. This will take care of de/serializing from POJO or protobuf objects seamlessly.

kalium.on(Event.class, event -> {

   //doSomething with the event
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM