The language-level support lock objects and threads are simply simple to write a thread security class. This article uses a simple programming example to illustrate how effective and intuitive development efficient thread security classes.
The Java programming language provides powerful language support for writing multithreaded applications. However, multithreading programs that are useful and no errors are still more difficult. This article tries to outline several methods, programmers can use these methods to create an efficient thread security class.
The programmer only benefits from multithreaded applications only when the problem is to be resolved needs to have some degree of concurrentness. For example, if the print queue application only supports a printer and a client, it should not be written as multithreaded. Generally, encoding problems comprising concurrency usually contains some operations that can be performed concurrently, and also contain some operations that cannot be executed. For example, a print queue for multiple clients and a printer provides a printed queue to support the printing concurrent request, but the output to the printer must be a serial form. Multithreading implementation can also improve the response time of interactive applications.
Synchronized Keyword Although most of the operations in multi-threaded applications can be conducted in parallel, there are also some operations (such as updating global flags or handling sharing files) that cannot be done in parallel. In these cases, a lock must be obtained to prevent other threads from accessing the same method before performing this operation. In the Java program, this lock is provided through the synchronized keyword. Listing 1 illustrates its usage.
Listing 1. Use the synchronized keyword to get the lock
Public class maxscore {
Int max;
Public maxscore () {
MAX = 0;
}
Public synchronized void currentscore (int S) {
IF (s> max) {
MAX = S;
}
}
Public int max () {
Return Max;
}
}
Here, the two threads cannot call the currentscore () method at the same time; when a thread works, the other thread must block. However, you can have any number of threads to access the maximum value through the max () method because max () is not a synchronization method, so it is independent of the lock. Try considering the impact of another method in the MaxScore class, the implementation of this method is shown in Listing 2.
Listing 2. Add another method
Public synchronized void reset () {
MAX = 0;
}
This method (when accessed) not only blocks other calls of the reset () method, but also blocks the currentscore () method in the same instance of the MaxScore class, because both methods access the same lock. If the two methods must be blocked from each other, the programmer must use synchronization at a lower level. Listing 3 is another situation, two of which may require independence with each other.
Listing 3. Two independent synchronization methods
Import java.util. *;
Public class jury {
Vector members;
Vector alternates;
Public Jury () {
MEMBERS = New Vector (12, 1);
Alternates = new vector (12, 1);
}
Public synchronized void addmember (String name) {
MEMBERS.ADD (Name);
}
Public synchronized void addalt (String name) {
Alternates.Add (Name);
}
Public synchronized vector all () {
Vector retval = new vector; members;
RetVal.Addall (Alternates); Return RetVal;
}
}
Here, two different threads can add members and alternates to the Jury object. Keep in mind that the Synchronized keyword can be used in the method, more general, or any code block. The two codes in Listing 4 are equivalent.
Listing 4. Equivalent code
Synchronized void f () {void f () {
// Execute some operations Synchronized (this) {
} // Execute some operations
}
}
Therefore, in order to ensure that the addmember () and addalt () methods are blocked from each other, you can override the Jury class in Listing 5.
Listing 5. After rewritten, the Jury class
Import java.util. *;
Public class jury {
Vector members;
Vector alternates;
Public Jury () {
MEMBERS = New Vector (12, 1);
Alternates = new vector (12, 1);
}
Public void addmember (string name) {
SYNCHRONIZED (MEMBERS) {
MEMBERS.ADD (Name);
}
}
Public void addalt (String name) {
Synchronized (alternates) {
Alternates.Add (Name);
}
}
Public vector all () {
Vector retval;
SYNCHRONIZED (MEMBERS) {
Retval = new vector; MEMBERS
}
Synchronized (alternates) {
RetVal.Addall (alternates);
}
Return RetVal;
}
}
Please note that we must also modify all () methods because it is meaningless to synchronize the Jury object. In the rewritable version, addmember (), addalt (), and all () methods only access the lock associated with MEMBERS and Alternates objects, so locking the Jury object is useless. Also note that all () methods can be written as in the form shown in Listing 6.
Listing 6. Use MEMBERS and Alternates as synchronous objects
Public vector all () {
SYNCHRONIZED (MEMBERS) {
Synchronized (alternates) {
Vector retval;
Retval = new vector; MEMBERS
RetVal.Addall (alternates);
}
}
Return RetVal;
}
However, because we have previously obtained the lock of MEMBERS and Alternates, this is not high. The rewriting form in Listing 5 is a better example because it only holds a lock in the shortest time, and only one lock is obtained each time. This completely avoids potential deadlock problems that may be generated when the code will be increased later.
Decomposition of the synchronization method is just as seen in the previous, the synchronization method obtains a lock of the object. If the method is frequently called by different threads, this method will become a bottleneck because it will restrict parallelism, which will restrict efficiency. In this way, as a general principle, the synchronization method should be used as much as possible. Despite this principle, sometimes a method may need to complete a few tasks that need to be locked, while also completing a considerable amount of other tasks. In these cases, a dynamic "Lock-Release-Lock-Release" method can be used. For example, Listing 7 and Listing 8 show code that can be converted in this way. Listing 7. Original low efficiency code
Public synchonized void dowork () {
Unsafe1 ();
Write_file ();
Unsafe2 ();
}
Listing 8. Code of high efficiency after rewriting
Public void doork () {
SYNCHONIZED (THIS) {
Unsafe1 ();
}
Write_file ();
SYNCHONIZED (THIS) {
Unsafe2 ();
}
}
Listing 7 and Listing 8 Assume that the first and third methods require objects to be locked, while more time-consuming WRITE_FILE () methods do not require object being locked. If you see, after reminding this method, the lock of this object is released after the first method is completed, and then re-obtained when the third method needs. Thus, any other method waiting for the lock of this object is still running when the WRITE_FILE () method is executed. Decompose synchronous methods into this mixed code can significantly improve performance. However, you need to pay attention to not introducing logical errors in this code.
The nesting internal class implements a focus on the Java program that allows the entire class to nested in another class. Nested classes as a member variable containing the class of it. If a particular method that is regularly called requires a class, you can construct a nested class. The only task of this nested class is to regularly call the required methods. This eliminates the dependence of other parts of the program and makes the code to modrew. Listing 9, a graphical clock basis, an internal class is used.
Listing 9. Graphical clock sample
PUBLIC CLASS CLOCK {
Protected class refresher extends thread {
Int refreshtime;
Public refresher (int x) {
Super ("Refresher");
Refreshtime = x;
}
Public void run () {
While (true) {
Try {
SLEEP (Refreshtime);
}
Catch (Exception E) {}
Repaint ();
}
}
}
Public clock () {
Refresher R = New Refresher (1000);
R.Start ();
}
Private void repaint () {
// Get time-consuming system call
/ / Re-draw the clock pointer
}
}
The code example in Listing 9 does not rely on any other code to call the repaint () method. In this way, it is quite simple to incorporate a clock into a larger user interface.
Event Driver Process When the application needs to reflect events or conditions (internal and external), there are two methods or to design the system. In the first method (called polling), the system determines this state periodically and is reflected accordingly. This method (although simple) is not efficient, because you can't predict when you need to call it. The second method (called event drive processing) is high, but it is also relatively complicated. In the event of an event-driven process, a letter is required to control when a particular thread should run. In the Java program, you can send signals to threads using Wait (), Notify () and NotifyAll () methods. These methods allow the thread to block on an object until the required conditions are satisfied, and then start running again. This design reduces the CPU occupancy because the thread does not consume the execution time during blocking, and can be woken up immediately when the notify () method is called. Compared to polling, the event drive method can provide a shorter response time. To create an efficient thread security class, the easiest way to write a thread security class is to declare each method with Synchronized. Although this solution can eliminate data damage, it also eliminates any benefits you expect from multi-threaded. This way, you need to analyze and make sure you only take only a minimum execution time inside the Synchronized block. You must pay special attention to access Slow Resources - Files, Directory, Network Sockets, and Database - these methods may reduce your programs efficiency. Try to put access to such resources in a separate thread, it is best to be outside of any SYNCHRONIZED code.
An example of a thread security class is designed as the center repository of files to be processed. It works with a set of threads that use getWork () and finishwork () with Worktable classes. This example is designed to experience a full-featured thread security class that uses the Helper thread and mixed synchronization. Note that the Usage of the Refresher Helper thread to continue adding the new file to be processed. This example is not adjusted to the best performance, it is clear that there are many places to rewrite to improve performance, such as rewriting the Refresher thread to using Wait () / notify () method to rewrite the populateTable () method to reduce the listing disk The effects of the file (this is a high cost operation).
Summary The multi-thread programming in the Java program is quite simple by using all the language supports available. However, making thread security classes have higher efficiency. In order to improve performance, you must use the lock function in advance and cautiously.