I'm working on an event manager, and I am wanting it to be a tool developers use. It is lightweight and it uses annotations to register events. I've tried to set up synchronization, and I'd like to ask if I'm doing it right so far. It's on GitHub.The main class that I would worry about for synchronization would be the EventManager class:// JavaDocs at GitHub Repo (if needed)public class EventManager {    private static volatile Logger logger = Logger.getLogger(EGEventManager);    private static volatile List<Class<? extends Event>> eventClasses = new ArrayList<>();    private static volatile List<EventListener> registeredListeners = new ArrayList<>();    ...    public static synchronized boolean registerEventClass(Class<? extends Event> event) {        if (!eventClasses.contains(event)) {            eventClasses.add(event);            return true;        }        return false;    }    ...    public static synchronized boolean unregisterEventClass(Class<? extends Event> event) {        if (eventClasses.contains(event)) {            eventClasses.remove(event);            return true;        }        return false;    }    ...    public static synchronized boolean isEventClassRegistered(Class<? extends Event> event) {        return eventClasses.contains(event);    }    ...    public static synchronized List<RegisteredEvent> registerEventListener(EventListener listener) {        List<RegisteredEvent> newlyRegistered = null;        if (!registeredListeners.contains(listener)) {            newlyRegistered = registerEventHandlers(listener);        }        return newlyRegistered;    }    @SuppressWarnings(unchecked)    private static synchronized List<RegisteredEvent> registerEventHandlers(EventListener listener) {        List<RegisteredEvent> newlyRegistered = new ArrayList<>();        try {            Class<? extends EventListener> eventListenerClass = listener.getClass();            Method[] classMethods = eventListenerClass.getDeclaredMethods();            for (int i = 0; i < classMethods.length; i++) {                Method method = classMethods[i];                if (method.getParameterCount() != 1) continue;                EventHandler[] methodAnnotations = method.getDeclaredAnnotationsByType(EventHandler.class);                if (methodAnnotations.length == 0) continue;                EventHandler eventHandlerAnnotation = methodAnnotations[0];                EventPriority priority = eventHandlerAnnotation.value();                Class<? extends Event> eventClass = (Class<? extends Event>) method.getParameterTypes()[0];                PrioritizedEvents.addRegisteredEvent(new RegisteredEvent(listener, method, eventClass, priority));            }        } catch (Exception e) {            e.printStackTrace();        }        return newlyRegistered;    }    ...    public static synchronized boolean unregisterEventListener(EventListener listener) {        if (registeredListeners.contains(listener)) {            registeredListeners.remove(listener);            return true;        }        return false;    }    ...    public static synchronized void call(EventExecutor eventExecutor, Class<? extends Event> eventClass, Object... eventArgs) {        try {            if (!eventClasses.contains(eventClass)) {                logger.warning(EventManager.call(Class<? extends Event>) cancelled: event is not contained in the registered Event classes!);                return;            }            List<RegisteredEvent> lowPriority = PrioritizedEvents.getRegisteredEvents(EventPriority.Low);            List<RegisteredEvent> normalPriority = PrioritizedEvents.getRegisteredEvents(EventPriority.Normal);            List<RegisteredEvent> highPriority = PrioritizedEvents.getRegisteredEvents(EventPriority.High);            Event eventInstance = null;            Class<?>[] constructorParameters = EventUtilities.getArrayOfClasses(eventArgs);            Constructor<?> constructor = eventClass.getDeclaredConstructor(constructorParameters);            eventInstance = (Event) constructor.newInstance(eventArgs);            for (int i = 0; i < lowPriority.size(); i++) {                RegisteredEvent registeredEvent = lowPriority.get(i);                EventListener listener = registeredEvent.getListener();                Method method = registeredEvent.getMethod();                if (registeredEvent.getEventClass() != eventClass) continue;                method.invoke(listener, eventInstance);            }            for (int i = 0; i < normalPriority.size(); i++) {                RegisteredEvent registeredEvent = normalPriority.get(i);                EventListener listener = registeredEvent.getListener();                Method method = registeredEvent.getMethod();                if (registeredEvent.getEventClass() != eventClass) continue;                method.invoke(listener, eventInstance);            }            for (int i = 0; i < highPriority.size(); i++) {                RegisteredEvent registeredEvent = highPriority.get(i);                EventListener listener = registeredEvent.getListener();                Method method = registeredEvent.getMethod();                if (registeredEvent.getEventClass() != eventClass) continue;                method.invoke(listener, eventInstance);            }            if (!eventInstance.isCancelled()) eventExecutor.execute();        } catch (Exception e) {            e.printStackTrace();        }    }    ...    static class PrioritizedEvents {        private static volatile Map<EventPriority, List<RegisteredEvent>> prioritized = new HashMap<>();        static {            EventPriority[] values = EventPriority.values();            for (int i = 0; i < values.length; i++) {                EventPriority priority = values[i];                prioritized.put(priority, new ArrayList<>());            }        }        ...        public static synchronized List<RegisteredEvent> getRegisteredEvents(EventPriority priority) {            return prioritized.get(priority);        }        ...        public static synchronized boolean addRegisteredEvent(RegisteredEvent registeredEvent) {            getRegisteredEvents(registeredEvent.getPriority()).add(registeredEvent);            return true;        }    }Things in this EventManager class like the java.util.Logger probably don't need to be volatile, but please correct me on this.For some things like the RegisteredEvent class, I expect that it would need to be synchronized. But in some of the classes, I'm thinking not everything needs to be tagged with the volatile keyword.// Full code available at GitHub (if needed)public class RegisteredEvent {    private EventListener listener;    private Method method;    private Class<? extends Event> eventClass;    private EventPriority priority;    ...    public RegisteredEvent(EventListener listener, Method method, Class<? extends Event> eventClass, EventPriority priority) {        this.listener = listener;        this.method = method;        this.eventClass = eventClass;        this.priority = priority;    }    ...    public EventListener getListener() {        return listener;    }    ...    public Method getMethod() {        return method;    }    ...    public Class<?> getEventClass() {        return eventClass;    }    ...    public EventPriority getPriority() {        return priority;    }}
Synchronization in an event manager
java;thread safety;callback;synchronization
There is no need to use the volatile keyword in the EventManager class. All methods are synchronized, so it is thread-safe(it is guaranteed that only one thread can execute only one method at a time and all changes made by this thread are visible to other threads that execute any method of this class in the future).Some things like the RegisteredEvent class, I expect that it would need to be synchronized, but in some of the classes, I'm thinking not everything needs to be tagged with the volatile keyword. Thread-safety is about controlling an access to a shared state. It's up to you to specify which parts of you system can be accessed from multiple threads(and how) and make decisions based on that. Do not arbitrary tag variables with the volatile keyword. Do not arbitrary add the synchronized keyword to some methods of some classes. You are very unlikely to get it right this way. The best way to go is to get a solid understanding of the Java memory model.