The question of the Philosopher's meal
One of the toughest problems with concurrent execution is the deadlock, the most classic example of a deadlock problem is the question of the Philosopher's meal: 5 philosophers sit on a table with 5 chopsticks on the table, each with a chopstick on the left and right hand side of each philosopher. As follows:
The question of the Philosopher's meal
One of the toughest problems with concurrent execution is the deadlock, the most classic example of a deadlock problem is the question of the Philosopher's meal: 5 philosophers sit on a table with 5 chopsticks on the table, each with a chopstick on the left and right hand side of each philosopher. As follows:
Philosophers must pick up both sides of the chopsticks to eat, if they also pick up the left side of the chopsticks, will lead to deadlock. Because the chopsticks on the right-hand side were picked up by the philosopher on his right, who had no way to dine, so that the five philosophers were dead locked.
Let's simulate this deadlock with code:
classPhilosopherImplementsRunnable {Private intID; PublicPhilosopher (intID) { This. ID =ID; } Public voidrun () {intLeftcsindex =ID; intRightcsindex = (id+1)%5; synchronized(Philotest.chopsticks[leftcsindex]) {System.out.println ("I got left Chopstick"); Try{thread.sleep (100);}Catch(Exception e) {}synchronized(Philotest.chopsticks[rightcsindex]) {System.out.println ("I got right Chopstick"); System.out.println ("Philosopher" + id+ ": Eating"); } } }} Public classPhilotest { Public Staticobject[] Chopsticks =NewObject[5]; Public Static voidMain (string[] args) { for(inti=0; i < chopsticks.length; i++) {Chopsticks[i]=NewObject (); } executorservice exec=Executors.newcachedthreadpool (); for(inti=0; I < 5; i++) {Exec.execute (Newphilosopher (i)); } exec.shutdown (); }}
The output is as follows, and the program never exits:
Philosopher0:i got left Chopstick
Philosopher2:i got left Chopstick
Philosopher1:i got left Chopstick
Philosopher3:i got left Chopstick
Philosopher4:i got left Chopstick
We created an array of length 5 to simulate chopsticks. In addition we define "philosopher threads", each philosopher has its own number, and we assume that the left-hand chopstick of the philosopher corresponds to an object with the same index in the array as the philosopher's number. The chopsticks on the right of the philosopher correspond to an object indexed by the philosopher number plus one in the array (note: The 4th philosopher on the right hand side of the chopsticks corresponds to the No. 0 object in the array). Each philosopher first picked up the chopsticks on the left, in order to ensure that all philosophers had got the chopsticks on the left, each philosopher had to wait 100 milliseconds after taking the chopsticks on the left, and then picked up the chopsticks on the right, when they were locked.
Conditions for deadlocks
There are four conditions for deadlocks, and it is necessary for each condition to be satisfied that a deadlock can occur, as long as one of the conditions is destroyed and no deadlock occurs.
1 Mutex: The resources the thread requests to obtain cannot be shared. In the above example, each philosopher does not share a single chopstick with another philosopher, and the code is that each "Philosopher's thread" is mutually exclusive with the lock, and a philosopher gets the lock of the object, and the other philosopher cannot get the lock of the object.
2. Hold and wait: The thread does not release the resources it has already held while applying for other resources. In the example above, the philosopher will hold the chopsticks on the left while trying to fetch the chopsticks on the right.
3. Cannot preempt: A thread-held resource cannot be preempted by another thread. In the example above, the philosopher can only take the chopsticks on the table and cannot steal chopsticks from other philosophers.
4. Cyclic waiting: In the example above, the No. 0 philosopher waits for the 1th philosopher to put down chopsticks, 1th philosopher and so on 2nd philosopher put down chopsticks .... The 4th philosopher waits for the No. 0 philosopher to put down his chopsticks, thus forming a circular wait.
Avoid deadlocks
The simplest way to avoid deadlocks is to break the cycle of waiting, for example, when a philosopher of 5 philosophers first went to the chopsticks on the right, and then took the chopsticks on the left, thus destroying the circular waiting. The instance code is as follows:
Public classSolvedeadlock { Public Staticobject[] Chopsticks =NewObject[5]; Public Static voidMain (string[] args) { for(inti=0; i < chopsticks.length; i++) {Chopsticks[i]=NewObject (); } executorservice exec=Executors.newcachedthreadpool (); for(inti=0; I < 4; i++) {Exec.execute (Newphilosopher (i)); } exec.shutdown (); intLeftcsindex = 4; intRightcsindex = 0; synchronized(Solvedeadlock.chopsticks[rightcsindex]) {System.out.println ("Philosopher4:i got right Chopstick"); Try{thread.sleep (100);}Catch(Exception e) {}synchronized(Solvedeadlock.chopsticks[leftcsindex]) {System.out.println ("Philosopher4:i got left Chopstick"); System.out.println ("Philosopher4:eating"); } } }}
Output Result:
Philosopher0:i got left Chopstick
Philosopher2:i got left Chopstick
Philosopher1:i got left Chopstick
Philosopher3:i got left Chopstick
Philosopher3:i got right Chopstick
Philosopher3:eating
Philosopher2:i got right Chopstick
Philosopher2:eating
Philosopher1:i got right Chopstick
Philosopher1:eating
Philosopher0:i got right Chopstick
Philosopher0:eating
Philosopher4:i got right Chopstick
Philosopher4:i got left Chopstick
Philosopher4:eating
In the example above we modified the main () method, using the main thread as the 4th philosopher, and the fourth philosopher took the chopsticks on the right and the chopsticks on the left. This avoids the loop waiting, so there is no deadlock this time. In the case of a philosopher's meal, mutual exclusion and holding and waiting are not avoidable, because these two are logically required, for example, two philosophers using a chopstick at the same time is contrary to common sense. So in addition to the fourth condition, we can avoid deadlocks by preemption. For example: The design of a "rude philosopher", if the philosopher did not get chopsticks, will go to other philosophers to grab chopsticks, so that the philosopher can be sure to eat, once he put down chopsticks, only 4 philosophers need to eat, and the table has 5 chopsticks, it will certainly not deadlock. For space reasons, this is not a code implementation, and interested readers can try to implement the idea.
Summarize
In multi-threaded systems, many of the probabilistic problems are caused by deadlocks between threads, which cause some threads to never stop executing, although these threads remain blocked, but still occupy memory space, which causes the memory space occupied by the thread to never be freed, which is the legendary memory leak. Thread deadlock is an important cause of memory leaks in Java applications. Therefore, it is important to avoid deadlocks when writing code, the simplest way to avoid deadlocks is to sort resources, and all threads access to resources in order, thus avoiding loops and avoiding deadlocks.
Public number: Today said yards. Follow my public number to see the serial article. If you do not understand the problem, directly in the public message can be.
Philosophers must pick up both sides of the chopsticks to eat, if they also pick up the left side of the chopsticks, will lead to deadlock. Because the chopsticks on the right-hand side were picked up by the philosopher on his right, who had no way to dine, so that the five philosophers were dead locked.
Let's simulate this deadlock with code:
classPhilosopherImplementsRunnable {Private intID; PublicPhilosopher (intID) { This. ID =ID; } Public voidrun () {intLeftcsindex =ID; intRightcsindex = (id+1)%5; synchronized(Philotest.chopsticks[leftcsindex]) {System.out.println ("I got left Chopstick"); Try{thread.sleep (100);}Catch(Exception e) {}synchronized(Philotest.chopsticks[rightcsindex]) {System.out.println ("I got right Chopstick"); System.out.println ("Philosopher" + id+ ": Eating"); } } }} Public classPhilotest { Public Staticobject[] Chopsticks =NewObject[5]; Public Static voidMain (string[] args) { for(inti=0; i < chopsticks.length; i++) {Chopsticks[i]=NewObject (); } executorservice exec=Executors.newcachedthreadpool (); for(inti=0; I < 5; i++) {Exec.execute (Newphilosopher (i)); } exec.shutdown (); }}
The output is as follows, and the program never exits:
Philosopher0:i got left Chopstick
Philosopher2:i got left Chopstick
Philosopher1:i got left Chopstick
Philosopher3:i got left Chopstick
Philosopher4:i got left Chopstick
We created an array of length 5 to simulate chopsticks. In addition we define "philosopher threads", each philosopher has its own number, and we assume that the left-hand chopstick of the philosopher corresponds to an object with the same index in the array as the philosopher's number. The chopsticks on the right of the philosopher correspond to an object indexed by the philosopher number plus one in the array (note: The 4th philosopher on the right hand side of the chopsticks corresponds to the No. 0 object in the array). Each philosopher first picked up the chopsticks on the left, in order to ensure that all philosophers had got the chopsticks on the left, each philosopher had to wait 100 milliseconds after taking the chopsticks on the left, and then picked up the chopsticks on the right, when they were locked.
Conditions for deadlocks
There are four conditions for deadlocks, and it is necessary for each condition to be satisfied that a deadlock can occur, as long as one of the conditions is destroyed and no deadlock occurs.
1 Mutex: The resources the thread requests to obtain cannot be shared. In the above example, each philosopher does not share a single chopstick with another philosopher, and the code is that each "Philosopher's thread" is mutually exclusive with the lock, and a philosopher gets the lock of the object, and the other philosopher cannot get the lock of the object.
2. Hold and wait: The thread does not release the resources it has already held while applying for other resources. In the example above, the philosopher will hold the chopsticks on the left while trying to fetch the chopsticks on the right.
3. Cannot preempt: A thread-held resource cannot be preempted by another thread. In the example above, the philosopher can only take the chopsticks on the table and cannot steal chopsticks from other philosophers.
4. Cyclic waiting: In the example above, the No. 0 philosopher waits for the 1th philosopher to put down chopsticks, 1th philosopher and so on 2nd philosopher put down chopsticks .... The 4th philosopher waits for the No. 0 philosopher to put down his chopsticks, thus forming a circular wait.
Avoid deadlocks
The simplest way to avoid deadlocks is to break the cycle of waiting, for example, when a philosopher of 5 philosophers first went to the chopsticks on the right, and then took the chopsticks on the left, thus destroying the circular waiting. The instance code is as follows:
Public classSolvedeadlock { Public Staticobject[] Chopsticks =NewObject[5]; Public Static voidMain (string[] args) { for(inti=0; i < chopsticks.length; i++) {Chopsticks[i]=NewObject (); } executorservice exec=Executors.newcachedthreadpool (); for(inti=0; I < 4; i++) {Exec.execute (Newphilosopher (i)); } exec.shutdown (); intLeftcsindex = 4; intRightcsindex = 0; synchronized(Solvedeadlock.chopsticks[rightcsindex]) {System.out.println ("Philosopher4:i got right Chopstick"); Try{thread.sleep (100);}Catch(Exception e) {}synchronized(Solvedeadlock.chopsticks[leftcsindex]) {System.out.println ("Philosopher4:i got left Chopstick"); System.out.println ("Philosopher4:eating"); } } }}
Output Result:
Philosopher0:i got left Chopstick
Philosopher2:i got left Chopstick
Philosopher1:i got left Chopstick
Philosopher3:i got left Chopstick
Philosopher3:i got right Chopstick
Philosopher3:eating
Philosopher2:i got right Chopstick
Philosopher2:eating
Philosopher1:i got right Chopstick
Philosopher1:eating
Philosopher0:i got right Chopstick
Philosopher0:eating
Philosopher4:i got right Chopstick
Philosopher4:i got left Chopstick
Philosopher4:eating
In the example above we modified the main () method, using the main thread as the 4th philosopher, and the fourth philosopher took the chopsticks on the right and the chopsticks on the left. This avoids the loop waiting, so there is no deadlock this time. In the case of a philosopher's meal, mutual exclusion and holding and waiting are not avoidable, because these two are logically required, for example, two philosophers using a chopstick at the same time is contrary to common sense. So in addition to the fourth condition, we can avoid deadlocks by preemption. For example: The design of a "rude philosopher", if the philosopher did not get chopsticks, will go to other philosophers to grab chopsticks, so that the philosopher can be sure to eat, once he put down chopsticks, only 4 philosophers need to eat, and the table has 5 chopsticks, it will certainly not deadlock. For space reasons, this is not a code implementation, and interested readers can try to implement the idea.
Summarize
In multi-threaded systems, many of the probabilistic problems are caused by deadlocks between threads, which cause some threads to never stop executing, although these threads remain blocked, but still occupy memory space, which causes the memory space occupied by the thread to never be freed, which is the legendary memory leak. Thread deadlock is an important cause of memory leaks in Java applications. Therefore, it is important to avoid deadlocks when writing code, the simplest way to avoid deadlocks is to sort resources, and all threads access to resources in order, thus avoiding loops and avoiding deadlocks.
Public number: Today said yards. Follow my public number to see the serial article. If you do not understand the problem, directly in the public message can be.
Java Concurrency Programming (10) deadlock