Even though the native methods are written in C, they still execute within the context of the Java environment, including multithreading. Native methods will sometimes have sections of code that modify global (class or application) data, which modify important state variables, or which must call non-re-entrant functions. (Some platform-specific APIs fall into this category.) These sections are called critical sections, because it is critical that no more than one thread executes the section of code at a time. Critical sections are not unique to native methods: the same issues exist when dealing with multithreading in pure Java code.
In order to ensure that the native methods maintain the application in a consistent state, these native methods will have to be synchronized with other threads. The simplest way to accomplish this is to declare the native methods as synchronized in the Java code. Sometimes, however, this will be insufficient, either for performance reasons (for example, a method that does a long computation, but only changes the condition variable infrequently), or because a multithreaded application needs to use an existing object that does not have synchronized methods (most do not).
In these cases, your native code can perform the synchronization directly. In Java code, you could put the critical section inside a synchronized block. The native method analogue to a synchronized block directly uses a monitor.
Monitors prevent two threads from simultaneously executing a section of code. Monitors were first introduced in C.A.R. Hoare's seminal paper "Communicating Sequential Processes" (Communications of the ACM, Vol. 21, No. 8, August 1978). Typically, each condition variable has a monitor associated with it. The monitor acts as a lock on that data. Unless a thread holds the monitor, it cannot modify or inspect that data. Obviously, a thread should hold the monitor as briefly as possible.
Note |
In Java, critical sections are usually methods. You can synchronize blocks of code smaller than methods. However, this leads to complex code, with many failure modes. Deadlock prevention at the method level is fairly straightforward, but it becomes rapidly more difficult when dealing with many small blocks of synchronized code. Even when dealing with native methods, it is best to use synchronization at the method level. |
Monitors provide the Java runtime system support for thread synchronization. Every object instance has a unique monitor attached to it. In this case, the entire object is considered a condition variable. Through a trio of functions, native methods can also use monitors.
void MonitorWait(unsigned int, int);void MonitorNotify(unsigned int);void MonitorNotifyAll(unsigned int);
These functions are analogous to the wait(), notify(), and notifyAll()functions in Java:
monitorWait | This function blocks the executing thread until the monitor is notified. If you encounter a deadlock, the first place to start is to check each occurrence of monitorWait(). |
monitorNotify | This function awakens no more than one waiting thread. It signals that the monitor is now available. It must only be called from the thread that holds the monitor. If an unhandled exception occurs while a thread holds the monitor, any threads waiting on the monitor will be blocked indefinitely. |
monitorNotifyAll | This function awakens all threads waiting on the monitor. It signals that the monitor is now available. |
Tip |
Monitors are re-entrant, which means that a thread that already holds a monitor will not deadlock if it calls monitorWait again. Monitor operations are atomic. They are not subject to race conditions (although the code that calls them is). |
The Java implementation of monitors has a nice feature that helps to prevent deadlock. Consider this scenario: a synchronized native method calls monitorWait, to wait for another synchronized method (native or not) to notify the monitor. Because the first method is synchronized, it already holds the monitor when monitorWaitis called. How is the second method ever supposed to execute? The implementation of monitorWaitmakes this possible by releasing the monitor on entry. This allows the second method to execute and notify the monitor. Once the monitor is notified, the first thread awakens. On exit from monitorWait, the first thread acquires the monitor again.
All three of the monitor functions are declared as receiving an unsigned integer. To get the monitor for an object, use the obj_monitormacro on the object's handle.
Here is an example of a synchronized string. This is an example of a producer/consumer problem. (Most synchronization problems can be reduced to one of two classic examples, producer/consumer and the colorfully named "dining philosophers problem." In a producer/consumer problem, one thread produces data items while another consumes them. Both operations must be synchronized so that the consumer gets each item exactly once. Listing 19.9 contains the Java definition of the synchronized string class, SyncString. Listing 19.10 shows the native method implementations of the SyncString.Setand SyncString.Get methods. Note once again that this could easily have been accomplished without the use of native methods. You will find that native methods are only useful in a very small set of circumstances.
Listing 19.9. Java class for SyncString.
class SyncString {String str;int bAvail;public native synchronized void Set(String s);public native synchronized String Get();static {try {System.loadLibrary("SyncString");} catch (UnsatisfiedLinkError e) {System.err.println("Cannot find library SyncString.");System.exit(-1);}}}
Listing 19.10. Native language implementation for SyncString.
#include <StubPreamble.h>#include "SyncString.h"void SyncString_Set(struct HSyncString *this,struct Hjava_lang_String *str){while(unhand(this)->bAvail == TRUE)monitorNotify(obj_monitor(this));unhand(this)->bAvail = TRUE;unhand(this)->str = str;monitorNotify(obj_monitor(this));}struct Hjava_lang_String *SyncString_Get(struct HSyncString *this){Hjava_lang_String *hstrTemp;while(unhand(this)->bAvail == FALSE)monitorWait(obj_monitor(this));unhand(this)->bAvail = FALSE;hstrTemp = unhand(this)->str;monitorNotify(obj_monitor(this));return hstrTemp;}
Note |
There are other functions that allow native code to construct new monitors not associated with an object. Use of these functions is strongly discouraged, due to the increasing difficulty of deadlock prevention in the face of many monitors. |
No comments:
Post a Comment