Over a million developers have joined DZone.

Low GC Coding: Efficient Listeners (Exercise)

· Java Zone

Learn more about how the Java language, tools and frameworks have been the foundation of countless enterprise systems, brought to you in partnership with Salesforce.

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.

 

Discover how the Force.com Web Services Connector (WSC) is a code-generation tool and runtime library for use with Force.com Web services, brought to you in partnership with Salesforce.

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 }}