Java multithreading practice

Source: Internet
Author: User

 Thread creation and startup
  
The Java language has built-in multi-thread support. All classes that implement the runnable interface can start a new thread. The new thread will execute the run () method of the instance. When run () after the method is executed, the thread ends. Once a thread is executed, the instance cannot be restarted. You can only generate a new instance and start a new thread.
  
The thread class is an instance that implements the runnable interface and represents a thread instance. The only way to start a thread is through the START () instance method of the thread class:
  
Thread t = new thread ();
T. Start ();
  
The START () method is a native method that starts a new thread and runs the run () method. The default run () method of the thread class exits without doing anything. Note: directly calling the run () method does not start a new thread. It is no different from calling a common Java method.
  
Therefore, there are two ways to implement your own thread:
  
Method 1: your own extend Thread class, and re-write the run () method, you can start a new thread and execute your own defined run () method. For example:
  
Public class mythread extends thread {
Public run (){
System. Out. println ("mythread. Run ()");
}
}
  
Start the thread in the appropriate place: New mythread (). Start ();
  
Method 2: If your class has extends another class, you cannot directly extends thread. At this time, you must implement a runnable interface:
  
Public class mythread extends otherclass implements runnable {
Public run (){
System. Out. println ("mythread. Run ()");
}
}
  
To start mythread, you must first instantiate a thread and input your own mythread instance:
  
Mythread MYT = new mythread ();
Thread t = new thread (MYT );
T. Start ();
  
In fact, after passing in a runnable target parameter to the thread, the thread's run () method will call target. Run (). Refer to the JDK source code:
  
Public void run (){
If (target! = NULL ){
Target. Run ();
}
}
  
The thread has some other settings, such as name, threadgroup, and isdaemon, which are rarely associated with the thread design mode.
  
  Thread Synchronization
  
Because multiple threads in the same process share memory space, in Java, it is a shared instance. When multiple threads attempt to modify the content of an instance at the same time, a conflict occurs, threads must implement shared mutex to synchronize multiple threads.
  
The simplest synchronization method is to mark a method as synchronized. For the same instance, only one synchronized method can be executed at any time. When a method is executing a synchronized method, if other threads want to execute any of the synchronized methods of this instance, they must wait until the thread executing the Synchronized Method exits this method, can be executed in sequence.
  
However, the non-synchronized method is not affected. No matter whether or not the synchronized method is executed, the non-synchronized method can be executed by multiple threads at the same time.
  
In addition, you must note that only the synchronized methods of the same instance can be executed by only one thread at a time, and the synchronized methods of different instances can be concurrent. For example, if class A defines the synchronized Method sync (), different instances of a1.sync () and a2.sync () can be executed by two threads at the same time.
  
  Java Lock Mechanism
  
The implementation of multi-thread synchronization ultimately depends on the lock mechanism. We can imagine that a shared resource is a room and everyone is a thread. When A wants to enter the room, he must get the door lock. Once A gets the door lock, he will immediately lock the door after entering, so B, C, D... you have to wait outside the door until A releases the lock, B, C, D... one of them grabbed the lock (the specific method depends on the implementation of JVM, You can first come, first served, or randomly selected), then entered the room and locked the door. In this way, at most one person can be in the House (using shared resources) at any time ).
  
Java language specifications have built-in support for multithreading. For Java programs, each object instance has a "Lock". Once a thread acquires the lock, if other threads want to obtain the lock, they can only wait for the thread to release the lock. There is only one way to obtain the lock, that is, the synchronized keyword. For example:
  
Public class SharedResource {
Private int count = 0;
  
Public int getCount () {return count ;}
  
Public synchronized void setCount (int count) {this. count = count ;}
  
}
  
Synchronization Method public synchronized void setCount (int count) {this. count = count;} is actually equivalent:
  
Public void setCount (int count ){
Synchronized (this) {// obtain this lock here
This. count = count;
} // Release this lock here
}
  
The red part indicates the code segment to be synchronized. This area is a "dangerous area". If two or more threads run simultaneously, a conflict occurs. Therefore, you need to change the internal status of SharedResource, you must first obtain the lock of the SharedResource instance.
  
When the synchronized block is exited, the lock owned by the thread is automatically released, so other threads can obtain the lock again.
  
To improve performance, you do not have to lock this. For example, SharedResource has two independent variables:
  
Public class SharedResouce {
Private int a = 0;
Private int B = 0;
  
Public synchronized void setA (int a) {this. a = ;}
  
Public synchronized void setB (int B) {this. B = B ;}
}
  
If the entire method is synchronized, setB () and setB () cannot be synchronized during setA (). To improve performance, you can use the locks of different objects:
  
Public class SharedResouce {
Private int a = 0;
Private int B = 0;
Private Object sync_a = new Object ();
Private Object sync_ B = new Object ();
  
Public void setA (int ){
Synchronized (sync_a ){
This. a =;
}
}
  
Public synchronized void setB (int B ){
Synchronized (sync_ B ){
This. B = B;
}
}
}

Wait ()/notify ()
  
Usually, coordination is required between multiple threads. For example, if displayThread of a browser that displays an image wants to execute the task of displaying the image, it must wait for the download thread downloadThread to download the image. If the image has not been downloaded, displayThread can be paused. After downloadThread completes the task, it notifies displayThread that "the image is ready and displayed". At this time, displayThread continues to run.
  
The above logic simply says: if the conditions are not met, wait. When the condition is met, the thread waiting for the condition will be awakened. In Java, the implementation of this mechanism depends on wait/notify. The wait and lock mechanisms are closely related. For example:
  
Synchronized (obj ){
While (! Condition ){
Obj. wait ();
}
Obj. doSomething ();
}
  
After thread A acquires the obj lock, it finds that the condition is not met and cannot continue the next processing. Therefore, thread A is wait ().
  
In another thread B, if B modifies some conditions so that the condition of thread A is met, thread A can be awakened:
  
Synchronized (OBJ ){
Condition = true;
OBJ. Y ();
}
  
  Note the following concepts:
  
# The OBJ lock must be obtained before calling the wait () and notify () Methods of OBJ, that is, it must be written in the code segment of synchronized (OBJ.
  
# Call obj. after wait (), thread a releases the OBJ lock. Otherwise, thread B cannot obtain the OBJ lock, and it cannot be in synchronized (OBJ ){...} wake up a in the code segment.
  
# After the obj. Wait () method is returned, thread a needs to obtain the OBJ lock again to continue execution.
  
# If both A1, A2, and A3 are in OBJ. wait (), B calls obj. notify () can only wake up one of A1, A2, and A3 (which one is determined by JVM ).
  
# Obj. notifyall () can all wake up A1, A2, A3, but continue to execute obj. the next statement of wait () must obtain the OBJ lock. Therefore, only one of A1, A2, and A3 has the opportunity to obtain the lock for further execution. For example, A1, the rest can be executed only after A1 releases the OBJ lock.
  
# When B calls obj. Y/policyall, B is holding the OBJ lock. Therefore, although A1, A2, and A3 are awakened, they still cannot obtain the OBJ lock. After B exits the synchronized block and releases the OBJ lock, only one of A1, A2, and A3 will have the opportunity to obtain the lock and continue execution.
  
  Differences between wait () and sleep ()
  
As mentioned above, the wait/notify mechanism and thread also has a static sleep () method, which can also suspend the thread for a period of time. The difference between sleep and wait is that sleep does not release the lock, and the pause of sleep is different from that of wait. OBJ. Wait will make the thread enter the waiting set of the OBJ object and wait for awakening.
  
However, both wait () and sleep () can interrupt the pause state of the thread through the interrupt () method, so that the thread immediately throws interruptedexception.
  
If Thread A wants to terminate Thread B immediately, it can call the interrupt method for the Thread instance corresponding to Thread B. If thread B is wait/sleep/join at the moment, thread B will immediately throw InterruptedException and directly return in catch () {} to end the thread safely.
  
It should be noted that InterruptedException is thrown by the thread itself from the internal, not by the interrupt () method. When interrupt () is called for a thread, if the thread is executing common code, the thread will not throw InterruptedException. However, once the thread enters wait ()/sleep ()/join (), InterruptedException is immediately thrown.
  
  GuardedSuspention
  
The main idea of the GuardedSuspention mode is:
  
When the condition is not met, the thread waits until the condition is met and the thread waiting for the condition is awakened.
  
We design a client thread and a server thread. The client thread continuously sends requests to the server thread, and the server thread continuously processes requests. When the request queue is empty, the server thread must wait until the client sends the request.
  
First define a request Queue: Queue
  
Package com. crackj2ee. thread;
  
Import java. util .*;
  
Public class Queue {
Private List queue = new queue List ();
  
Public synchronized Request getRequest (){
While (queue. size () = 0 ){
Try {
This. wait ();
}
Catch (InterruptedException ie ){
Return null;
}
}
Return (Request) queue. remove (0 );
}
  
Public synchronized void putRequest (Request request ){
Queue. add (request );
This. policyall ();
}
  
}
  
The blue part is the waiting condition of the server thread. After the client thread puts a request, the server thread waits for the condition to be met, and then the server thread is awakened.
  
Client thread: ClientThread
  
Package com. crackj2ee. thread;
  
Public class ClientThread extends Thread {
Private Queue queue;
Private String clientName;
  
Public ClientThread (Queue queue, String clientName ){
This. queue = queue;
This. clientName = clientName;
}
  
Public String toString (){
Return "[ClientThread-" + clientName + "]";
}
  
Public void run (){
For (int I = 0; I <100; I ++ ){
Request request = new Request ("" + (long) (Math. random () * 10000 ));
System. out. println (this + "send request:" + request );
Queue. putRequest (request );
Try {
Thread. sleep (long) (Math. random () * 10000 + 1000 ));
}
Catch (InterruptedException ie ){
}
}
System. out. println (this + "shutdown .");
}
}
  
Server thread: ServerThread
  
Package com. crackj2ee. thread;
Public class ServerThread extends Thread {
Private boolean stop = false;
Private Queue queue;
  
Public ServerThread (Queue queue ){
This. Queue = queue;
}
  
Public void Shutdown (){
Stop = true;
This. Interrupt ();
Try {
This. Join ();
}
Catch (interruptedexception IE ){}
}
  
Public void run (){
While (! Stop ){
Request request = queue. getrequest ();
System. Out. println ("[serverthread] handle request:" + request );
Try {
Thread. Sleep (2000 );
}
Catch (interruptedexception IE ){}
}
System. Out. println ("[serverthread] shutdown .");
}
}
  
The server thread may be blocked in the red part, that is, queue. getrequest is a blocking method. This is similar to many Io methods in the java standard library.
  
  Finally, write a Main program to start them:
  
Package com. crackj2ee. thread;
  
Public class main {
  
Public static void main (String [] args ){
Queue queue = new Queue ();
ServerThread server = new ServerThread (queue );
Server. start ();
ClientThread [] clients = new ClientThread [5];
For (int I = 0; I <clients. length; I ++ ){
Clients [I] = new ClientThread (queue, "" + I );
Clients [I]. start ();
}
Try {
Thread. sleep (100000 );
}
Catch (InterruptedException ie ){}
Server. shutdown ();
}
}
  
We started five Client threads and one server thread. The running result is as follows:
  
[ClientThread-0] send request: Request-4984
[ServerThread] handle request: Request-4984
[ClientThread-1] send request: Request-2020
[ClientThread-2] send request: Request-8980
[ClientThread-3] Send request: Request-5044
[ClientThread-4] Send request: Request-548
[ClientThread-4] Send request: Request-6832
[Serverthread] handle request: Request-2020
[Serverthread] handle request: Request-8980
[Serverthread] handle request: Request-5044
[Serverthread] handle request: Request-548
[ClientThread-4] Send request: Request-1681
[ClientThread-0] Send request: Request-7859
[ClientThread-3] Send request: Request-3926
[Serverthread] handle request: Request-6832
[ClientThread-2] Send request: Request-9906
......
  
We can see that serverthread processes requests from different clients.
  
  Thoughts
  
Q: Can I change the server thread's wait condition while (queue. Size () = 0) to If (queue. Size () = 0 )?
  
A: In this example, we can use only one server thread. However, if there are multiple server threads (for example, the Web application has multiple threads to process concurrent requests, which is very common), it will cause serious problems.
  
Q: Can I use sleep (1000) instead of wait ()?
  
A: Absolutely not. Sleep () does not release the lock, so other threads cannot call getRequest () and putRequest () during sleep, resulting in all related threads being blocked.
  
Q: (Request) queue. remove (0) can be placed outside the synchronized () {} block?
  
A: No. Because while () is the test queue, remove () is the use of queue, both are an atomic operation, and cannot be placed outside synchronized.
  
  Summary
  
Multi-threaded design seems simple. In fact, you must carefully consider various locking/synchronization conditions. If you are not careful, you may make mistakes!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.