Java notes: familiar with Thread Technology-thread collaboration and deadlock issues in the basics (Part II)

Source: Internet
Author: User

The topic of this article is thread collaboration and deadlock.

Thread collaboration I personally think is thread communication. For example, if there are two threads a and B, both A and B can run independently, and a and B sometimes exchange information, this is the thread collaboration. In Java, thread collaboration is implemented through the "handshake mechanism" between threads. This handshake mechanism is implemented through wait () and Policy () in the object class..

In my memory,Sleep (), wait () and Policy () (policyall ())The answer is the interviewer's favorite question. Next I will start from the relationship between these methods to the question of thread collaboration.

The sleep () method belongs to the Thread class, and the wait () and Policy () (policyall () Methods belong to the object class..

The above is the first principle I want to talk about. Here I will emphasize the following:The sleep () method belongs to the Thread class, and the wait () and Policy () (policyall () Methods belong to the object class..

I am at the frontArticleSaid,Objects in Java naturally contain a lock, which belongs to the object class rather than the Thread class.So here there is another principle:The lock is not released when sleep () is called. The sleep () and wait () methods can both stop threads, but the nature of the two methods is different. The wait () method can release the lock while the sleep () method () the lock cannot be released. This feature is very important. The lock mechanism ensures thread security and achieves thread synchronization. If the sleep () method cannot release the lock, it means that the sleep () method cannot control thread synchronization, when the wait method is used, other methods that are locked for synchronization can be called. Therefore, the wait () method can control thread synchronization.,As you can see, the use of the wait () method affects the use of other threads. This is the so-called thread collaboration. Likewise, for policy () and policyall () they will wake up the waiting thread, that is, let the thread get the current object lock, and notify other threads to pause. This is also a kind of thread collaboration..

In short, waiting (wait () and notification (notify () are a way of thread collaboration.

The wait () method of the object class has two forms. The first is to accept the number of milliseconds as the parameter. The meaning of this usage is the same as that of the parameter in the sleep () method, all are expressed as "paused in a certain period of time ". But there are also differences: one is the issue of releasing the lock above, and the second thread that is waiting by wait () can pass Policy (), policyall (), or the wait status will be restored after the expiration time.

The second method is wait () without any parameters, which is more common. Wait () enables the thread to wait infinitely until the thread receives the notify () or notifyall () message.

The wait (), Policy (), and policyall () methods are part of the object class rather than part of the thread class. It is really strange to have a look at this, this approach makes sense,Granting the lock mechanism to an object as an inseparable property will help us extend the thread application idea. At least put these methods into the object, we can put the wait method into any method with synchronization control function, without considering whether the class of the method inherits the thread or implements the runnable interface. However, you must note that the sleep (), wait (), Policy (), and policyall () methods can only be used to call wait (), Policy () in the synchronous control method or synchronization block () and policyall () methods, because these operations will use the lock, and the operation that does not operate the lock will be non-synchronous control method, we can call the sleep () method. The following are the problems we often make accidentally: if we call the wait (), Y (), and notifyall () methods in non-synchronous methods,ProgramThe code will be compiled, but the program will report an illegalmonitorstateexception exception during runtime. It will also contain some confusing prompts, such as: the current thread is not the owner, which means: Calling wait () the thread of the notify () and notifyall () methods must own the Lock of this object before calling these methods..

To understand the three methods of wait (), notify (), and notifyall (), the key lies in the wait () method. Generally, under what circumstances do we use the wait () method, the text below is my summary of him:

When you write a thread with a synchronization nature, there is a thread A. Let's pause thread a first. When the program runs to a certain time point, we need thread a to start again to work, what is thread a doing at this time? It is waiting for a condition to activate it, and the condition for activating it must be another thread, such as line B, which should be used to stop thread, to wake up a thread that is stopped by the wait method, you must use the Y or notifyall method..

Below I write a SectionCodeTo demonstrate wait and notify usage:

Package cn.com. sxia ;

Class order {
Private Static int I = 0 ;
Private int COUNT = I ++ ;

Public Order (){
If (COUNT = 10 ){
System. Out. println ("food is gone, hitting ") ;
System. Exit (0) ;
}
}

@ Override
Public String tostring (){
Return "Order [Count =" + Count + "]" ;
}
}

Class waitperson extends thread {
Private Restaurant ;

Public waitperson (restaurant R ){
Restaurant = r ;
Start () ;
}

Public void run (){
While (true ){
While (restaurant. Order = NULL ){
Synchronized (this ){
Try {
Wait () ;
} Catch (interruptedexception e ){
E. printstacktrace () ;
}
}
System. Out. println ("the waiter got the order:" + restaurant. Order) ;
Restaurant. Order = NULL ;
}
}
}
}

Class Chef extends thread {
Private Restaurant;
Private waitperson ;

Public chef (restaurant R, waitperson W ){
Restaurant = r ;
Waitperson = W ;
Start () ;
}

Public void run (){
While (true ){
If (restaurant. Order = NULL ){
Restaurant. Order = New Order () ;
System. Out. println ("place an order ") ;
Synchronized (waitperson ){
Waitperson. Policy () ;
}
}
Try {
Sleep (100) ;
} Catch (interruptedexception e ){
E. printstacktrace () ;
}
}
}
}

Public class restaurant {

Order order ;

Public static void main (string [] ARGs ){
Restaurant restaurant = new restaurant () ;
Waitperson = new waitperson (restaurant) ;
Chef chef = new chef (Restaurant, waitperson) ;

}

}

The result is as follows:

Place order
Order: Order [Count = 0]
Place order
Order: Order [Count = 1]
Place order
Order: Order [Count = 2]
Place order
Order: Order [Count = 3]
Place order
Order: Order [Count = 4]
Place order
Order: Order [Count = 5]
Place order
Order: Order [Count = 6]
Place order
Order: Order [Count = 7]
Place order
Order: Order [Count = 8]
Place order
Order: Order [Count = 9]
Food is gone.

The logic of the program is roughly like this: There is a restaurant with only one chef and one waitperson in it. The waiter must wait for the cook to prepare food, when the cook completes the food, the cook will inform the waiter that the food is ready. The waiter will receive the food and distribute it to the guest. Then the cook will receive the order and tell the cook that the cook will continue waiting until the cook finishes all the 10 food items.

This is a typical example of thread collaboration: Producer (Cook)-consumer () Waiter.

In the program, waitperson must first obtain the order restaurant from the restaurant. order, and then call the wait method in the waitperson run method to keep the thread of the waitperson object in the waiting state until the chef object wakes up by running y. Because we write a simple program, we know that a waitperson is waiting to be awakened. If there are multiple waitperson waiters waiting for the same lock, we have to use the policyall method, notifyall will wake up all the threads waiting for the lock, and the thread that responds to the result of the Cook needs to be coordinated by itself. As for the chef object, he must know the order of the restaurant and the waitperson object of the food to be taken away. This ensures that the related waiter will be notified after the food in the order is prepared, you can see that the notify method is called in the run method of Chef. Here I want to talk about the details: When the run method in chef calls the waitperson's notify method, the chef object will get the lock of the waitperson object, while the waitperson object that originally executed wait () will release its own lock, and the notify method has unique control over the lock when calling it, this ensures that thread conflicts are not caused when multiple threads have the same object's y method.

We can see from the content in this example:One thread operates on an object, and the other thread can use the object operated by the previous thread in a certain way. In this typical thread, in the "producer-consumer" mode, the "first-in-first-out" queue method is used to implement the producer and consumer models..

There are more ways to implement thread collaboration in Java, but the above method is the most commonly used. As for other implementations, my current information is incomplete, I will write in the advanced thread article about comprehensive data collection.

Now I want to talk about deadlocks. Before talking about deadlocks, you must first understand the thread status.

The thread status is divided into five categories:

    1. New): The thread object has been created, but it has not been started. Therefore, the newly created thread cannot run;
    2. Runnable): In this state, the thread can run as long as the scheduling program of the thread allocates the time slice calculated by the CPU to the thread. This state is the same as the idling of the car. We know that the car has started, but it just didn't run. As long as we step down the accelerator a little, the car can fly immediately.
    3. Running): The thread has been started. The thread scheduling mechanism grants the CPU computing time, running status, and the thread is running in the run method;
    4. Dead): Thread death is usually caused by the run method.
    5. Blocked): The thread can run, but a condition prevents it from running. When a thread is in a blocking state, the scheduling mechanism of the thread will ignore the thread and will not assign any CPU computing time to the thread. Only when the thread enters the waiting state again, this thread can be executed.

We need to care about the blocking status. What causes thread blocking? Below is my summary:

    1. When the thread calls the sleep method so that the thread is suspended and waiting;
    2. Similarly, when the thread uses the wait method, the thread will also be blocked;
    3. The thread intends to call its synchronous control method on an object, but the Lock of this thread cannot be used.

The deadlock problem is caused by the combination of thread blocking and synchronization. Because the thread can be blocked and the object has a synchronous control method to prevent other threads from accessing the object when the lock is not released, the following problems will occur when the thread is running: when a is waiting for B to complete execution or B to know that he is released, and B is waiting for other threads, a will continue to extend, until a thread in the thread chain waits for the first thread, that is, thread A, to release its own lock, it is like an endless loop, and threads wait for each other to not communicate with each other, in the end, all threads cannot be executed. This is a "deadlock".

When talking about the deadlock of threads, we have to mention the classic deadlock phenomenon: dining by philosophers. The dining question of philosophers was raised by the famous computer scientist izger Dickus,The original problem was: there were five philosophers who spent part of their time thinking about the problem and eating part of their time. When they thought about it, they didn't need to share any resources, but when they were eating, however, they only have limited tableware at the table. If the food on the table is noodles, a philosopher needs two chopsticks. However, philosophers are very poor, so they only bought five chopsticks and Five chopsticks and shared them with five philosophers. When a philosopher eats, the philosopher must borrow a chopsticks from his philosophical home on the left or right. When the philosopher who borrowed chopsticks eats, the philosopher who borrowed the chopsticks will have to wait..

The following is the philosopher's code:

 Package Cn.com. sxia;

Import Java. util. Random;

Class Chopstick {
Private Static Int Counter = 0;
Private Int Number = counter ++;

@ Override
Public String tostring (){
Return "Chopstick [number =" + number + "]";
}
}

Class Philosopher Extends Thread {
Private Static Random Rand = New Random ();
Private Static Int Counter = 0;
Private Int Number = counter ++;
Private Chopstick leftchopstick;
Private Chopstick rightchopstick;
Static Int Ponder = 0;

Public Philosopher (chopstick left, Chopstick right ){
Leftchopstick = left;
Rightchopstick = right;
Start ();
}

Public Void Think (){
System. Out. println ( This + "Thinking ");
If (Ponder> 0 ){
Try {
Sleep (RAND. nextint (ponder ));
} Catch (Interruptedexception e ){
E. printstacktrace ();
}
}
}

Public Void Eat (){
Synchronized (Leftchopstick ){
System. Out. println ( This + "Owned" + This . Leftchopstick + "Waiting" + rightchopstick );
Synchronized (Rightchopstick ){
System. Out. println ( This + "Eating ");
}
}
}

@ Override
Public String tostring (){
Return "Philosopher [number =" + number + "]";
}

Public Void Run (){
While ( True ){
Think ();
Eat ();
}
}
}

Public Class Diningphilosophers {

Public Static Void Main (string [] ARGs ){
If (ARGs. length> 3 ){
System. Out. println ("the number of input parameters is incorrect ");
System. Exit (1 );
}

Philosopher [] philosophers = New Philosopher [integer. parseint (ARGs [0])];
Philosopher. Ponder = integer. parseint (ARGs [1]);
Chopstick left = New Chopstick (), Right =New Chopstick (), first = left;
Int I = 0;
While (I <philosophers. Length-1 ){
Philosophers [I ++] = New Philosopher (left, right );
Left = right;
Right = New Chopstick ();
}
If (ARGs [2]. Equals ("deadlock ")){
Philosophers [I] = New Philosopher (left, first );
} Else {
Philosophers [I] = New Philosopher (first, left );
}

If (ARGs. length> = 4 ){
Int Delay = integer. parseint (ARGs [3]);
If (Delay! = 0 ){
// Timeout is the timeout framework I wrote earlier.
New Timeout (delay * 1000, "timed out ");
}
}
}

}

In the program, chopstick and philosopher both use an automatically added static counter to mark each generated object, every philosopher has a reference to the chopstick object on the left and right. Chopsticks are the tableware of philosophers before eating. The static variable ponder is used to indicate how much time the philosopher spends thinking. If the input value is 0, the thread sleep time in the think method is randomly generated. In the Eat method, philosophers get chopsticks on the left through synchronous control. If chopsticks are unavailable, philosophers will wait. below is my change to the philosopher's problem, let philosophers get two chopsticks to eat. When philosophers get the chopsticks on the left, they use the same method to get the chopsticks on the right. After eating, they release the chopsticks on the right to release the chopsticks on the left.

In the main method, we need to input parameters. If the number of parameters is smaller than three, the program will prompt that the number of parameters is incorrect and the program will exit. If the number of parameters is 3, if the number entered by the third parameter is N, the program will prompt timeout after n seconds. The correct number of parameters is 3, and the first parameter is used to specify the number of philosophers, the second parameter is used to specify the time for philosophers to think. The third parameter has two usage methods: one is the timeout time mentioned above, and the other is the deadlock parameter, which will cause the program to deadlock, for example, if I enter the 3, 20 and deadlock parameters, the result is as follows:

............................
Philosopher [number = 0] is being eaten
Philosopher [number = 0] Thinking
Philosopher [number = 0] Has chopstick [number = 0] waiting for chopstick [number = 1]
Philosopher [number = 0] is being eaten
Philosopher [number = 0] Thinking
Philosopher [number = 2] Has chopstick [number = 2] waiting for chopstick [number = 0]
Philosopher [number = 2] is being eaten
Philosopher [number = 2] Thinking
Philosopher [number = 0] Has chopstick [number = 0] waiting for chopstick [number = 1]
Philosopher [number = 1] Has chopstick [number = 1] waiting for chopstick [number = 2]
Philosopher [number = 2] Has chopstick [number = 2] waiting for chopstick [number = 0]

The cause of program deadlock is:The last philosopher was given chopsticks on the left and first chopsticks on the front. Since the last philosopher was sitting next to the first philosopher, they shared the first chopsticks, in this case, all philosophers are prepared to eat at a certain point in time, and a deadlock will eventually occur..

Deadlock is a phenomenon we are reluctant to see, and the deadlock problem is hidden. Sometimes it is hard to find it before the program delivery customers use it, it may be the best time for programmers to discover deadlocks. To avoid deadlocks, you need to know which conditions will lead to deadlocks. To generate deadlocks, four conditions must be met simultaneously:

    1. At least one resource used by the thread cannot be shared. For example, the chopsticks in the philosophers question above can only be used by one philosopher at a time. Such sharing is exclusive;
    2. After a thread holds resources, it is still waiting. This thread is waiting for another thread to release related resources.
    3. Shared resources cannot be preemptible by threads. In fact, threads must be synchronized;
    4. Threads must wait for resources, which is like an endless loop.

To solve the deadlock problem, we need to destroy one of the four conditions,It is difficult to control the deadlock from the language level. It can only be avoided by programmers who carefully design their own programs.This is a very troublesome process, but there is no other good solution.

Thread blocking is the source of many problems. Blocking is not an error, but sometimes it is difficult to correct the error because of thread blocking, so we can use a very violent method to interrupt the thread, A thread with a blocking interruption may be simply solved by the problems in our program, but this solution may provide a poor user experience. We can use the thread to interrupt the blocked thread. the following code is used to implement the interrupt () method:

 Package Cn.com. sxia;

Import Java. util. timer;
Import Java. util. timertask;

Class BlockedExtends Thread {
Public Blocked (){
System. Out. println ("thread blocking started ");
Start ();
}

Public Void Run (){

Try {
Synchronized ( This ){
Wait ();
}
} Catch (Interruptedexception e ){
System. Out. println ("interrupted operation ");
}
System. Out. println ("exit the run method ");
}
}

Public Class Interrupt {

Static Blocked blocked = New Blocked ();
Public Static Void Main (string [] ARGs ){
New Timer ( True ). Schedule ( New Timertask (){

@ Override
Public Void Run (){
System. Out. println ("prepare to interrupt the thread ");
Blocked. Interrupt ();
Blocked = Null ;
}
},2000 );
}

}

The result is as follows:

 
Thread blocking started
Prepare to interrupt the thread
Interrupted operation
Exit the run Method

The thread basics are explained, but the thread topic is not over yet. However, I have learned all the basics about the thread. For the thread, I will start a thread advanced article, the advanced article does not explain more difficult things, but describes some useful or interesting thread technologies.

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.