Previous | Next | Trail Map | Integrating Native Methods into Java Programs | Implementing Native Methods


Native Methods and Thread Synchronization

Sometimes, a native method contains a critical section of code--a section of code that accesses or modifies a condition variable. Like other critical sections or methods in Java, these native methods have to be synchronized with other methods or code blocks that access the same variable. Synchronization ensures that code running in different threads accesses the condition variable in safety and that the condition variable is in some globally consistent state. Threads of Control(in the Learning the Java Language trail)covers programming with threads. In particular, the page Multithreaded Programs(in the Learning the Java Language trail)covers issues related to writing programs that contain multiple threads, including how to synchronize them. You should be familiar with the concepts in that section before proceeding here.

Three utility functions declared in monitor.h allow native methods to interact with Java's monitor mechanism, so that native methods can be thread-safe like regular Java methods. Those functions are:

monitorWait()
Blocks until notification is made on the specified monitor. The monitorWait() function is equivalent to Object's wait() method.
monitorNotify()
Notifies one waiting thread that there is a change of condition on the specified monitor. The monitorNotify() function is equivalent to Object's notify() method.
monitorNotifyAll()
Notifies all waiting threads that there is a change of condition on the specified monitor. The monitorNotifyAll() function is equivalent to Object's notifyAll() method.
Let's look at an example program that uses these functions to synchronize the execution of two native methods. This example program is a modified version of the Producer/Consumer example found in Synchronizing Threads(in the Learning the Java Language trail).

The example program is comprised of these four classes:

Producer
A thread that produces integer values and puts them in a "cubby hole".
Consumer
A thread that consumes the values placed in the cubby hole by the producer.
ProducerConsumerTest
The main program that starts the producer and consumer threads.
CubbyHole
The object where the producer puts its values and the consumer gets them. In the previous, Java-only implementation of this example, the put() and get() methods in this class were synchronized. The program has been modified so that put() and get() are now native methods and use the monitorXXX() functions to synchronize the producer and consumer threads. This is the only class that needed to be modified.
Let's look at the new CubbyHole class. The changes made to the CubbyHole class are in bold.
class CubbyHole {
    private int seq;         // this is the condition variable.
    private boolean available = false;

    public synchronized native int get();
    public synchronized native void put(int value);

    static {
        try {
            System.loadLibrary("threadex");
        } catch (UnsatisfiedLinkError e) {
            System.err.println("can't find your library");
            System.exit(-1);
        }
    }
}
Here's the original version of the CubbyHole class for comparison.

The first change is that the get() and put() methods are now declared native (as well as synchronized) and their implementations have been removed. They are now implemented in C in the file CubbyHoleImpl.c.

The second change is the addition of a static initializer block. This block of code loads the dynamic library threadex that contains the implementations for the get() and put() methods.

Now, let's look at the new implementations for get() and put().

long CubbyHole_get(struct HCubbyHole *this) {
    while (unhand(this)->available == 0) {
        monitorWait(obj_monitor(this));
    }
    unhand(this)->available = 0;
    monitorNotify(obj_monitor(this));
    return unhand(this)->seq;
}

void CubbyHole_put(struct HCubbyHole *this, long value) {
    while (unhand(this)->available > 0) {
        monitorWait(obj_monitor(this));
    }
    unhand(this)->seq = value;
    unhand(this)->available = 1;
    monitorNotify(obj_monitor(this));
}
The logical flow of these methods is identical to the Java implementation of these methods. Note the use of monitorWait() and monitorNotify() is identical to the use of the wait() and notify() methods in the Java version.


Previous | Next | Trail Map | Integrating Native Methods into Java Programs | Implementing Native Methods