Exploring thread synchronization in JavaScript and Rhino
Still making my way through Concurrent Programming in Java by Doug Lea.
This book is great. Yes, great. The intended audience is intermediate to advanced programmers who want a deep understanding of common problems and their solutions in concurrent, object-oriented programming. Obviously, all the examples in the book are in Java, and many details specific to the Java language and standard libraries are presented, but the text is meant to serve a greater audience looking for general concurrent programming techniques.
In the section of the book where the basics of thread synchronization are being explained, Doug provides an example class called Even that would work perfectly well in a sequential (non-multi-threaded) environment, but is likely to fail in a multi-threaded one.
As usual, I wanted try this idea out with a Java scripting language just to see if anything interesting or unexpected happens…
Although JavaScript is not a multi-threaded language/environment, the Rhino JavaScript interpreter/scripting environment allows scripting of Java in JavaScript and access to all Java types such as Thread and Runnable. Thus, you can basically compose multi-threaded JavaScript.
So here is a port of the basic idea in section 2.2 of the book to JavaScript using Rhino. The code uses a class called Even which is meant to only represent even numbers. By instantiating and instance of Even and calling its next() method, the next even number is returned. However, since the next() method is not synchronized, it is possible for multiple threads to examine the instance in an illegal state (where next() returns an odd number).
importClass(java.lang.System,
java.lang.Thread,
java.lang.Runnable);
/**
* Class for generating even numbers. Not Thread-safe!!!
*/
function Even() {
this._n = 0;
}
/**
* Return the next even number. Broken Method...
* can return odd numbers in multi-threaded env.
* @returns number
*/
Even.prototype.next = function () {
this._n++;
this._n++;
return this._n;
};
/**
* Static utility method to determine if a given
* number is even.
* @throws IllegalArgumentException if n is not
* a number
* @param number n
* @returns boolean
*/
Even.isEven = function (n) {
if ('number' != typeof n)
throw 'IllegalArgumentException: Even.isEven only ' +
'accepts number args';
return 0 == n % 2;
};
var even = new Even();
var r = new Runnable() {
run: function () {
while (true) {
var x = even.next();
if (!Even.isEven(x)) {
throw 'RuntimeException: Thread synchronization' +
' is needed to ensure that instances of Even' +
' always return even numbers from their' +
' get() method';
}
System.out.println(Thread.currentThread().getName()+': '+x);
}
}
};
new Thread(r).start();
new Thread(r).start();
The problem arises in the next() method of the Even class. This method does not execute an atomic operation, but should!
At first glance, you might be tempted to make the following change to the next() method correct the problem:
Even.prototype.next = function () {
this._n += 2;
return this._n;
};
Unfortunately, this doesn’t work either. The += operator deceptively appears to execute an atomic operation, but it does not… it actually executes both a read and write operation. This method suffers from the same fatal flaw as the original implementation.
JavaScript (even with Rhino) does not have language-level support for synchronization like Java does through the synchronized keyword. So, at the language level, there is no fix for this broken class. However, Java 1.5 has been released with a new package of high-level concurrency utils java.util.concurrent which can be used to create the desired effect. This package was actually inspired by the package of production-ready concurrency utils provided with the Concurrent Programming book. These will run on Java 1.2.
Since I’m on Mac OS X Tiger, and have access to Java 1.5 Tiger (finally), let’s try to fix the situation using the new ReentrantLock class in java.util.concurrent.locks. Make the following change to the Event.next() instance method:
importClass(java.util.concurrent.locks.ReentrantLock);
/**
* Thread-safe class for generating even numbers.
*/
function Even() {
this._n = 0;
this.lock = new ReentrantLock();
}
/**
* Return the next even number.
* @returns number
*/
Even.prototype.next = function () {
this.lock.lock();
this._n++;
this._n++;
this.lock.unlock();
return this._n;
};
Now the script will never break the invariants of the Even class.
About this entry
You’re currently reading “Exploring thread synchronization in JavaScript and Rhino,” an entry on Todd Ditchendorf’s Blog.
- Published:
- 05.15.05 / 2pm
- Category:
- Java, JavaScript/DHTML, Rhino
8 Comments
Jump to comment form | comments rss [?] | trackback uri [?]