The main EventManager class synchronizes on triggering a new event. Then every event gets its own brand new thread. Then each newly spawned thread turns around and synchronizes to wait for the right to dispatch to the event's handlers. Then each handler gets its own brand new thread.
For m events with n handlers, that's mn+m threads. 100 events w/ 5 handlers? That's 600 new threads.
If you have frequent events or your handlers take any time at all, you're dead. This will simply grind to a halt under any kind of real load.
Thank you for your views on the project. Creating new threads may not be as expensive as you seem to think, -on a standard desktop computer you get between 5000-1000 executed events per second, which is probably sufficient for most situations. This use of multi-threading is used to achieve full asynchronisity. The opposite, as you are implying, is to execute the events sequentially, within the same thread. I apprechiate that you would sometimes prefer the latter, but this can in fact be very easily achieved by using a fixed size thread pool, and adjusting its size to your preferences. Setting the size to one, would ensure sequencial execution of all events. Maybe I should include a threadpool in the next version, allowing its size to be specified.
you get between 5000-1000 executed events per second
As a point of comparison, I put together a quick Python script that defines a simple single-threaded event dispatcher and then does the same thing as your testPerformance() test, and it handles over 296k events/s on my machine (where javaEventing handles 3.7k).
This use of multi-threading is used to achieve full asynchronisity
The way that you're currently synchronizing, the only concurrency you're seeing is for your handlers. You only trigger and dispatch out to handlers one event at a time.
There are two scenarios where threads are always going to be unavoidably slow: high contention, and processor-bound tasks with more threads than processors. Your current synchronization policy pretty much assures you're going to run into high contention, and if you're using this for processor-bound tasks, you're going to pay a severe tax for the number of threads you're generating, as well.
Put in the threadpool. Take a look at ConcurrentHashMap and CopyOnWriteArrayList. Work on your synchronization policy. High throughput concurrency and eventing is hard, but Java actually has a lot of nice building blocks to work with. If you want a good overview, you should check out Brian Goetz's Java Concurrency in Practice.
The updated version (from 1.3.0) of the library uses a thread-pool, and yields between 50.000-100.000 events/second. While I apprechiate your focus on performance, for most actual use this would be academic. In most cases, even 10.000 events per second would be sufficient.
First of all, thank you for your thoughts on my project. As Pohl points out, the anonymous handler-implementations are chosen by design, and are in my opinion very well suited for these types of situations. But I'm thinking maybe you're not bothered by the anonymous implementations, but by the fact that classes like the Event-interface and the GenericEventHandler classes are made as public inner classes within the EventManager class? My thought was that, as they are so tightly coupled to the use of the EventManager class, they might as well be defined inside the EventManager as inner classes. However, if you have any reasons for why they should be defined elsewhere, I'd apprechiate your opinions.
As to your concerns regarding the static use of the EventManger, I can apprechiate your point of view. I'll put it on my todo-list for the next version.
The static methods need to go. That's just bad form, because of unit tests, or the ability to use the decorator pattern to enhance an EventManager (or instrument one), or to make an EventManager injectable via something like Guice.
I'm not sure why you're bothered by inner classes, though. Anonymous inner classes are a very common way to write event-handling code in java, especially if the handler needs to capture some enclosing scope for later use.
For m events with n handlers, that's mn+m threads. 100 events w/ 5 handlers? That's 600 new threads.
If you have frequent events or your handlers take any time at all, you're dead. This will simply grind to a halt under any kind of real load.