Skip navigation

I've got some very cool news to share. The Props application can now be easily customized with integration into Nitro, Bunchball's gamification engine! This enables some very cools things. For example, the act of giving/getting props will now be incorporated into the status system in Jive so your points will be incremented. Also, you can construct a set of challenges and missions (Bunchball's terms) that encourage people to recognize employees. Ultimately, you enable Bunchball's "store", turning recognition from props into cold, hard, merchandise! Sweet!

 

Here's how it works... The first thing you do is setup the Nitro engine in Bunchball to receive events from Props, e.g. Props Given and Props Received. You then enable missions that will use these events.

Nitro_Admin_Console.png

 

Because we have the gamification profile extension enabled, when the the two new challenges are active, they will show up as badges you can earn.

Your_Profile_|_Jive.png

 

OK. So now, when I give a prop, the "Flowers" will show up as an earned "badge". When Akshay Narayan receives a props, his "Trophy" badge will be enabled. Check it out...

jive_devslanding_transparent.png

props-profile-after.pngakshay_props.png

 

SWEEET!

 

A Few Details

In order to enable this, you have to run your own version of Props because you need to customize the code to point it to your Nitro server. In addition, everyone will want to define their own missions based on giving/receiving props. Also, another request that we get is for custom trophies--another customization that is now enabled.

 

The Fine Print

Bunchball and Jive are very excited about being able to gamify applications, however, this is a new model and we need to make sure the technology side complements the business side. All of the code for props is available in our open source github repository today. However, in order to get the code that hooks in bunchball, you need to contact Jive--this code is not under an open source license but is available upon request. (Just send Mark Weitzel and/or Ryan Rutan a message & we'll coordinate with the Bunchball team.)

 

Last and definitely not least...

It's time to give credit where credit is due! The coding for customizing props was done by Akshay Narayan, one of Jive's very talented interns from University of California, Berkeley . He's returning to school this fall and Friday was his last day. His energy and enthusiasm will be missed. Along the way, he received some great technical advice and mentorship from Murali VP. Of course, this would not have been possible without Dan Katz from Bunchball and Curtis Gross seeing the potential of this idea to open up new innovation into Jive. And of course, there's our muse, the one and only Tracy Maurer.

Writing thread safe code is hard. As developers we really need to see the whole context all at once to be able to reason through all the potential execution orderings. The usage of locks are typically spread throughout a given class or classes which makes holding the whole context in your head difficult. I am going to present some guidelines and solutions to common Java locking problems that are make the code more readable.

Context for the following guidelines:


public class Foo {

    // Method level locks should be avoided
    synchronized void setFoo(int x, int y) {
        // do some field assignments
    }

    private final Object lock = new Object(); // Lock Object.
    void setFoo(int x, int y) {
        synchronized(lock) { // Better than method level locks.
            // do some field assignments
            instance.doSomething(x,y); // Should be avoided
        }
    }
   
    ReetrantLock reetrantLock;
    void setFoo(int x, int y) {
        try {
            reetrantLock.lock();
            // do some field assignments
        } finally {
            reetrantLock.unlock();
        }
    }
}


Guidelines:

  • Avoid using synchronized on methods. Instead use a lock object.
  • All lock objects must be final. No exceptions!
  • Try to avoid calling methods within a given synchronized block. If you have to then make them final or private. This will reduce the chance of unintentional nesting of locks.
  • Stick with old school synchronized unless you really need one of the features of the java.util.concurrent.locks. In general needing a feature of java.util.concurrent.locks is a code smell that your solution is overly complex. In general an old school synchronized block with a lock object is more readable. Yes reetrant locks can be faster when there is high contention however I suggest reading When to use reetrant locks.
  • Locks can frequently be avoided by using classes from java.util.concurrent and java.util.concurrent.atomic.


One of the key pivot points for when to use a reetrant lock is high contention. There are many times when you can and should partition your problem across a collection of locks.

 

Here is a pathological example for a method that would have very high lock contention.

 

public class Foo {

    private final Object lock = new Object(); // Lock Object.
    void updateUsersGeoLocation(String tenantId,long userId, double lat, double lon) {
        synchronized(lock) {
            Tenant tenant = db.getTenant(tenantId);
            User user = tenant.getUser(userId);
            user.setLat(lat);
            user.setLon(lon)
        }
    }
   
    LatLon getUsersGeoLocation(String tenantId, long userId) {
        synchronized (lock) {
            Tenant tenant = db.getTenant(tenantId);
            User user = tenant.getUser(userId);
            return new LatLon(user.getLat(), user.getLon());
        }
    }
}


In a perfect world we would have access to the User class and could put the locking there. For this example the User class is a third party class that cannot be changed.

 

So how can this contention be reduced? There are two levels of lookups. The first gets the Tenant instance for a given tenantId and the second gets a User instance for a given userId. If only we had a way to have a lock per tenant and a lock per user. If you look around the web you will find a lot of very complex solutions to this problem. Here is a very simple solution. The LocksProvider class is a nice way to create a bag of locks that can decongest a hot lock. It relies on using a key that has a good hashcode distribution. You don’t create a lock for every user and every tenantId. Instead you create N times the number of threads you know will be changing the Users state. The advantage to this solution is you can increase the number of locks to decrease the odds of contention on a given lock. You don’t have to solve any of the problems created by using the actual tenantId or userId as the lock. This solution to locking is sometimes called striped locking.


public class LocksProvider<K> {

    private final Object[] locks;

    public LocksProvider(int numLocks) {
        locks = new Object[numLocks];
        for (int i = 0; i < numLocks; i++) {
            locks[i] = new Object();
        }
    }

    public Object lock(K key) {
        return locks[Math.abs(key.hashCode() % locks.length)];
    }
}


Using the LocksProvider class we can rewrite the pathological example to look something like this.


public class Foo {

    private final LocksProvider<String> tenantLocks = new LocksProvider<>(10);
    private final LocksProvider<Long> userLocks = new LocksProvider<>(10);

    void updateUsersGeoLocation(String tenantId, long userId, double lat, double lon) {
        Tenant tenant = getTenant(tenantId);
        synchronized (userLocks.lock(userId)) {
            User user = tenant.getUser(userId);
            user.setLat(lat);
            user.setLong(lon)
        }
    }

    LatLon getUsersGeoLocation(String tenantId, long userId) {
        Tenant tenant = getTenant(tenantId);
        synchronized (userLocks.lock(userId)) {
            User user = tenant.getUser(userId);
            return new LatLon(user.getLat(), user.getLon());
        }
    }
    private Tenant getTenant(String tenantId) {
        synchronized (tenantLocks.lock(tenantId)) {
            return db.getTenant(tenantId);
        }
    }
}


We now have an implementation that will have much less lock contention. If your use case still proves to have very high contention and you cannot control the hash distribution of your key then you can replace the ”private final Object[] locks;” in LocksProvider with “private final ReetrantLock[] locks;” to leverage the performance or functionality of ReentrantLocks.


If you made it this far you clearly have an interest in locking. For further reading I suggest you check out http://en.wikipedia.org/wiki/Lamport's_bakery_algorithm.

Filter Blog

By date: By tag: