From https://www.cnblogs.com/niuyourou/p/12004183.html
Section I The philosopher's dining problem
Section 2 What is a deadlock
Section 3 Definition of deadlock
Section 4 Conditions for deadlock
Section 5 How to avoid deadlock
5.1 Dynamic avoidance, banker algorithm (leverage allocation), chapter in the context of resource allocation
5.2 Static avoidance, avoid deadlock from task code
Section 6 Comprehensive Treatment of Deadlock
Section I The philosopher's dining problem
Suppose five philosophers sit around a round dining table and do one of two things: eat, or think. When they eat, they stop thinking, and they stop eating when they think. There is a large bowl of pasta in the middle of the dining table, and there is a pair of chopsticks between every two philosophers. Because it is difficult to eat pasta with one chopstick, it is assumed that the philosopher must eat with two chopsticks. They can only use the two chopsticks on their left and right. As shown below:
Each philosopher picked up two chopsticks on the left and right, five philosophers and five pairs of chopsticks. The philosophers happily ate the noodles and thanked me by the way, so the question ended. (Huh? It seems wrong)
The real problem is this, there is only one chopstick instead of a pair between every two philosophers , the problem is shown in the figure below:
Philosophers must eat with two chopsticks, and they can only use the two chopsticks on their left and right.
For the above problem, we can naturally think of an algorithm:
1. Wait for the left chopsticks to be available, pick up the left chopsticks
2. Wait for the right chopsticks to be available, pick up the right chopsticks
3. Eat
4. Put down two chopsticks
Obviously, when the number of attempts is sufficient, there is a situation that cannot be avoided: every philosopher has picked up the chopsticks on the left and is waiting for the chopsticks on the right. Because the chopsticks on the right are in the hands of the philosopher on the right, and the philosopher on the right is also waiting for the philosopher on the right to put down the chopsticks, everyone holds each other ’s resources and waits for others to release. In a state of starvation, this is a deadlock.
Section 2 What is a deadlock
We know that the execution of threads requires resources, and each thread will use the resources in a certain order. If the request for resources is rejected, the thread cannot continue to execute downwards and will wait. There are always three steps for threads to use resources:
1. Request Resources
2. Use resources
3. Free up resources
In the operating system, a thread must wait when it fails to request a resource. There are two ways to wait. One is blocking waiting; it is non-blocking waiting, that is, returning immediately, doing other things, and then trying the request again, or failing to exit to terminate the thread.
If the thread waits for resources in a non-blocking manner, no deadlock will occur. It is conceivable that a philosopher requested the chopsticks on the right hand to fail, put down the chopsticks held in the left hand, give up eating , and continue to think. Then the philosopher on the left has two pairs of chopsticks. After eating and putting down the two pairs of chopsticks, the left and right philosophers can get two pairs of chopsticks. The whole situation of resource allocation will be alive, and no deadlock will occur.
However, if you use blocking methods to wait for resources, a deadlock may occur. Every philosopher holds chopsticks and waits for another chopstick to be available. Every philosopher will not put down the chopsticks in his hand, so everyone is endless. wait.
If there are n threads T1 ~ Tn, and n resources R1 ~ Rn. Among them, Ti holds the resource Ri, but requests the resource Ri + 1, so it becomes a deadlock. We can express this relationship of holding and waiting through a directed graph. The solid line indicates that the resource is already held, and the dashed line indicates that the resource is waiting, as shown in the following figure:
Each thread is waiting for a certain resource, so no thread can advance, thus causing a deadlock.
Section 3 Definition of deadlock
If a group of threads, each thread is waiting for an event (the event here usually refers to the release of resources), and the event that each thread waits can only be issued by another thread in the thread group, then this group of threads is called A deadlock has occurred.
Let's look at a set of codes that obviously cause deadlock:
//线程1
new Thread(){
@Override
public void run(){
synchronized(A){
synchronized(B){
doSomeThing....
}
}
}
}
//线程2
new Thread(){
@Override
public void run(){
synchronized(B){
synchronized(A){
doSomeThing....
}
}
}
}
If thread 1 and thread 2 are executed alternately, 1 acquires lock A; then 2 executes and 2 acquires lock B.
1 Request B is rejected because B is held by 2; 2 Request A is rejected because A is held by 1.
So far, 1, 2 can not be executed, forming the following loop waiting:
We are not saying that the program must be deadlocked. If thread 1 acquires two locks before thread 2 starts executing, no deadlock will occur. But when threads 1 and 2 run alternately, the probability of deadlock is much greater than the probability of no deadlock. We will not discuss how much the probability of the occurrence of a deadlock, we consider the problem of deadlock dimension is likely to occur deadlock with the impossible deadlock .
Of course, the program we write is best to prevent deadlock, which requires us to clearly define when deadlock will occur and avoid it, so what are the conditions for deadlock?
Section 4 Conditions for deadlock
The occurrence of deadlock must meet four conditions:
1. Limited resources . A very intuitive condition, like the question in the first section, if there are five pairs of chopsticks on the table. Every philosopher can get enough chopsticks to eat at the same time, and deadlock will not happen.
2. Hold waiting . That is, when a thread requests a new resource, it does not release the acquired resource. If the philosopher requests that the chopsticks on the right hand fail, then put down the chopsticks on the left hand, then the philosopher on the left will have two chopsticks, and the resources will be activated.
3. Can't preempt . If a philosopher fails to request the chopsticks on the right hand, and then grabs the chopsticks on the right hand, then he will also get two chopsticks.
4. Wait in a loop . That is the example in the second section of the picture, you wait for me and I wait for you, everyone's waiting and holding relationship form a closed loop.
The above four conditions are indispensable for the formation of deadlock. As long as we break one of the conditions, the deadlock will not occur. So how do we break these conditions to avoid deadlock?
Section 5 How to avoid deadlock
There are two ways to avoid deadlock: dynamic avoidance and static avoidance .
5.1 Dynamic avoidance, banker algorithm, chapter in the context of resource allocation
Let me start with a more interesting example, the leverage of banks, let us see how banks avoid deadlocks.
Supplement: For banks, liquidity is resources (computer resources or semaphores), and lenders are resource requesters (threads).
We all know that the user must have a purpose to complete the loan. For simplicity, we assume that the user's purpose is to use money to do business, and the user abides by the credit (if you have money, you will repay).
If the user can borrow to the maximum value of his credit limit, it can meet the expectations of the user plan, the business can be done, and the user can repay on time.
If the user does not get the maximum value of the credit limit, it will cause the user's cost to be insufficient to successfully complete the business, and there will not be enough money to repay the loan, resulting in bad debts for the bank.
Banks have limited funds, so when lending, banks must consider whether there is a way to allocate their reserves to meet the maximum demand of each user, that is, the loan that meets the maximum credit limit of each user.
For example, suppose the bank has 12,000 liquid funds, and at this time, three people A, B, and C apply for a loan.
A credit limit is 4,000 yuan
B credit limit is 6000 yuan
C credit limit is 10,000 yuan
The loan requirements are:
A application for 2000 yuan
B application 4000 yuan
C application 3000 yuan
If the bank agrees and lends all at once, the bank will have only 3000 yuan of liquidity left. If C continues to apply for its remaining 7,000 yuan, the bank will have no money to deposit. C business failure caused the bank to face the risk of 3,000 yuan in bad debts.
If the bank only approves the requirements of A and B, the bank will have 6,000 yuan of liquidity left. No matter whether A applies for the remaining 2,000 yuan quota or B applies for the remaining 2,000 yuan quota, the bank can meet it. Then A and B can successfully complete the task repayment. After waiting for both A and B to complete the task and repay, then approve C's request, so that the remaining liquidity of the bank is 9,000 yuan, which can meet the remaining C 7,000 yuan quota requirement, and successfully support C to complete the business.
In other words, when reviewing the loan requirements, the bank must consider whether the remaining working capital can help all the users who have already loaned to get the maximum loan amount if they agree to the loan . That is to say, every time the loan is reviewed, the bank must consider whether the loan will put the bank into an insecure state .
In this way, although everyone's credit limit will not exceed the bank's total liquidity, the total credit limit of everyone will be much greater than the bank's total liquidity, which is leverage . And under the leverage, each loan is carefully reviewed to dynamically allocate resources, so as to avoid funds falling into the lender's deadlock (the bank lends itself to each other after waiting for each other to repay the debt ).
The 2007 subprime mortgage crisis in the United States was an example of an algorithmic error that led to a deadlock. Many companies and individuals could not afford loans and led to the bankruptcy of a large number of banks.
The dynamic avoidance algorithm means that every time resource allocation is performed, it needs to be carefully calculated to ensure that the application of the resource will not cause the system to enter an unsafe state. Safe state means that we can find a method and sequence of resource allocation so that each running thread can get the resources it needs. If the allocation of resources will cause the system to enter an unsafe state, it is rejected. Dynamic avoidance is a means of preventing deadlock in the allocation of resources.
In fact very few programs adopt a strategy of dynamic avoidance. The first reason is the complexity of the algorithm. We need to maintain information such as the resource usage and the number of resources held by the thread and the number of resources required. The second reason is that it is difficult for us to predict in advance how many resources each thread needs; third The reason is that if there are a large number of threads, the calculation each time a resource is allocated will take a lot of time.
5.2 Static avoidance, avoid deadlock from task code
In Section 5.1, we discussed the strategy for dynamic avoidance of deadlock from resource allocation. Below we discuss how to avoid deadlock from task code.
1. Eliminate non-preemptive conditions. We turn resources into preemptible, and philosophers can grab chopsticks from each other. This strategy sounds feasible, but it is not universal. For example, if the preempted resource is not a chopstick but a lock, then the lock will lose its original semantics. Such consequences are unthinkable!
2. Eliminate holding waiting. This method is feasible. If the philosopher fails to request the chopsticks, he will put down the chopsticks in his hand and go to sleep. Chopsticks, either sleep and wait for other philosophers to wake up when they put down the chopsticks), the philosophers will get two chopsticks in sequence and go to dinner.
3. In the previous section, we discussed the use of banker algorithms for dynamic resource allocation. It is feasible to use it to solve philosopher problems, but the algorithm will be more complicated. We will solve this problem from the perspective of resource allocation from another angle. The reason for the deadlock is that from a resource perspective, if five philosophers pick up a chopstick at the same time, then there will be no chopsticks on the table and no philosopher can eat. If we only allow up to four philosophers to pick up chopsticks at the same time, then at least one of the four philosophers can get enough chopsticks to eat (there is one chopstick left on the table) and the deadlock will not happen.
This solution actually belongs to the banker's algorithm, because when the four philosophers pick up the chopsticks, only one chopstick will remain on the table. The fifth philosopher tried to pick up chopsticks. We judged that if he was allowed to pick up chopsticks, the remaining resources would not be able to satisfy any philosopher who picked up two chopsticks to eat.
Because there are only two roles in this question, chopsticks and philosopher. That is, there is only one resource (chopsticks), so the dependency relationship is relatively simple, and we can staticize the banker's algorithm.
4. Eliminate the loop wait condition. As in the example in the third, we stipulate that all threads that need A and B need to obtain A before obtaining B, then deadlock will not occur. Because there will be no situation where a thread holds B and waits for A, there will be no loop waiting. For the philosopher's dining problem, we can also break the cycle of waiting by specifying the order in which chopsticks are taken. For example, we can number philosophers. Odd-numbered philosophers must first take the left-hand chopsticks, and even-numbered philosophers must first take the right-handed chopsticks, as shown below:
Section 6 Comprehensive Treatment of Deadlock
In the previous sections, we introduced the definition of deadlock, the conditions under which deadlock occurs, and how to avoid deadlock.
For the four necessary conditions for the deadlock to occur: limited resources, resources cannot be preempted, hold wait, and loop wait, as long as we break any of them, the deadlock will not occur.
Things don't look complicated, but the fact is that even experienced programmers can't completely avoid deadlocks when doing multi-programming.
The main reason is that when the number of resources and threads increases, it is difficult to determine their dependencies. We must find out and manage the deadlock situation from all possible dependencies, which is difficult to do in actual programming (except for simple cases).
For example, the philosopher's dining problem, the PV primitives (synchronization, mutual exclusion) may be very simple:
P(leftChopsticks)
P(rightChopsticks)
eat();
V(rightChopsticks)
V(leftChopsticks)
But in the case where the philosophers (threads) run alternately, there are many possibilities for their instantaneous dependencies.
In addition to designing by considering resource allocation, sequential access to resources, and non-holding waits, we try to avoid deadlocks as much as possible. When deadlocks occur, we must protect the site and conduct investigations at the first time. of).
We can view the vmid of the Java process that needs to be viewed through the jps command:
Then check the stack in the process through jstack:
Pull down, the information we need is below:
It can be seen that the jvm found a deadlock and a circular wait occurred between Thread-0 and Thread-1.
You can also use the jconsole command to view:
Click "Detect Deadlock":
Then you can view the status of the deadlock thread:
Transfer failed, re-upload canceled
Transfer failed,re-uploadcanceled
Transfer failed, re-upload canceled