Previous | Next | Trail Map | Using the JNI to Integrate Native Code and Java Programs | Java Native Interface Programming


Threads and Native Methods

Java is a multithreaded system, therefore native methods must be thread-safe programs. Unless you have extra knowledge (for example, the native method is synchronized), you must assume that there can be multiple threads of control running though a native method at any given time. Native methods therefore must not modify sensitive global variables in unprotected ways. That is, they must share and coordinate their access to variables in certain critical sections of code.

Before reading this section, you should be familiar with the concepts of threads of control and multithreaded programming. Threads of Control(in the Writing Java Programs trail)covers programming with threads. In particular, the page Multithreaded Programs(in the Writing Java Programs trail)covers issues related to writing programs that contain multiple threads, including how to synchronize them.

Threads and JNI

The JNI interface pointer (JNIEnv *) is only valid in the current thread. You must not pass the interface pointer from one thread to another, or cache an interface pointer and use it in multiple threads. The Java Virtual Machine will pass you the same interface pointer in consecutive invocations of a native method from the same thread, but different threads pass different interface pointers to native methods.

You must not pass local references from one thread to another. In particular, a local reference may become invalid before the other thread has had a chance to use it. Always convert them to global references when there is any doubt that a reference to Java object may be used by different threads.

Check the use of global variables carefully. Multiple threads might be accessing the global variables at the same time. Make sure that you put in appropriate locks to ensure safety.

Thread Synchronization in Native Methods

JNI provides two synchronization functions to allow you to implement synchronized blocks. In Java, they are implemented using the synchronized statement. For example:
synchronized (obj) {
   ...                   /* synchronized block */
}

The Java Virtual Machine guarantees that a thread must acquire the monitor associated with Java object obj before it can execute the statements in the block. Therefore, at any given time, there can be at most one thread running inside the synchronized block.

Native code can perform equivalent synchronization on objects using the JNI functions MonitorEnter and MonitorExit. For example:

(*env)->MonitorEnter(obj);
...                      /* synchronized block */
(*env)->MonitorExit(obj);
A thread must enter the monitor associated with obj before it can continue the execution. A thread is allowed to enter a monitor multiple times. The monitor contains a counter signaling how many times it has been entered by a given thread. MonitorEnter increments the counter when the thread enters a monitor it has already entered. MonitorExit decrements the counter. If the counter reaches 0, other threads can enter the monitor.

Wait and Notify

Another useful thread synchronization mechanism is Object.wait, Object.notify, and Object.notifyAll. The JNI does not directly support these functions. However, a native method can always follow the JNI method call mechanism to invoke these methods.


Previous | Next | Trail Map | Using the JNI to Integrate Native Code and Java Programs | Java Native Interface Programming