Over a million developers have joined DZone.

Low GC Coding: Efficient Listeners (Exercise)

· Java Zone

Navigate the Maze of the End-User Experience and pick up this APM Essential guide, brought to you in partnership with CA Technologies

Overview

There are many ways to implement a listener pattern depending on the assumptions you want to make.
This is an exercise to get you thinking about how these can be implemented efficiently.

Simple set of listeners

Implement a thread safe set of listeners which supports add and remove.  Assume there is *many* more events than add/removes and you want to optimize the notification use case.
 public interface Observer {
    void update(Observable o, Object arg);
}
public class Observable {
    public void addObserver(Observer o) {
    }
   publicvoid removeObserver(Observer o) {
    }
   publicvoid notifyObservers(Object arg) {
        // must be thread safe
        // no locks, no garbage
    }
}
In case you think this is an easy task, note that the built-in Observable uses both a lock and creates lots of garbage.

As a part of this task, you should use a set to ignore duplicates, but keep the order Observers were added. i.e. call them in this order.

Bonus Exercise

a) Compare the performance with the built-in Observable for 0, 1 and 10 Observers. Can you explain the results you get?

b) change the order each time the notifyObservers is called (or almost every time) so that each Observable has an equal change of being first. (do this without locks or creating garbage)

Solution

Solution to follow after some time to solve the problem yourself.

Possible solution - CopyOnWriteArraySet

Running the following with -XX:-UseTLAB -XX:+DoEscapeAnalysis -XX:+AggressiveOpts
public class TestIterations {
    public static void main(String... ignored) {
        CopyOnWriteArraySet observers = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 100; i++) {
            long before = memoryUsed();
            callObservers(observers);
            long size = memoryUsed() - before;
            System.out.printf("10000x Iterations used %,d bytes%n", size);
        }
    }
    private static void callObservers(CopyOnWriteArraySet observers) {
        for (int j = 0; j < 10000; j++) {
            for (Observer observer : observers) {
                observer.update(null, null);
            }
        }
    }
    static int counter = 0;
    static class MyObserver implements Observer {
        @Override
        public void update(Observable o, Object arg) {
            counter++;
        }
    }
    public static long memoryUsed() {
        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
    }
}
prints
10000x Iterations used 240,000 bytes
10000x Iterations used 240,000 bytes
10000x Iterations used 240,000 bytes
10000x Iterations used 240,000 bytes
10000x Iterations used 240,000 bytes
10000x Iterations used 240,000 bytes
10000x Iterations used 240,000 bytes
10000x Iterations used 240,000 bytes
10000x Iterations used 240,000 bytes
It is possible to avoid any garbage.

 

Thrive in the application economy with an APM model that is strategic. Be E.P.I.C. with CA APM.  Brought to you in partnership with CA Technologies.

Topics:

Published at DZone with permission of Peter Lawrey, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}