Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Low GC Coding: Efficient Listeners (Exercise)

DZone's Guide to

Low GC Coding: Efficient Listeners (Exercise)

· Java Zone ·
Free Resource

Verify, standardize, and correct the Big 4 + more– name, email, phone and global addresses – try our Data Quality APIs now at Melissa Developer Portal!

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.

 

Developers! Quickly and easily gain access to the tools and information you need! Explore, test and combine our data quality APIs at Melissa Developer Portal – home to tools that save time and boost revenue. Our APIs verify, standardize, and correct the Big 4 + more – name, email, phone and global addresses – to ensure accurate delivery, prevent blacklisting and identify risks in real-time.

Topics:

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}