Java concurrent Programming-various locks
Security and activity are often mutually constrained. We use locks to keep threads safe, but abusing locks can cause lock-order deadlocks. Similarly, we use thread pools and semaphores to constrain the use of resources,
But the lack of knowledge of which areas of activity may form a deadlock of resources. Java applications cannot recover from deadlocks, so it is valuable to make sure that your design is able to prevent deadlocks from appearing.
A. Deadlock
The classic "Philosopher's Dinner" problem illustrates the deadlock very well. 5 philosophers went out for lunch together, and they sat around a round table. They have only five chopsticks (not 5 pairs) and one in the middle of each two person.
Philosophers eat and think, alternately. Everyone needs to get two chopsticks to eat, but after eating to put the chopsticks back to continue to think. There are some algorithms to manage chopsticks so that everyone can more or less timely
Eat something (a hungry philosopher tries to get two of nearby chopsticks, but if one of them is being taken up by someone else, he likes to give up one of the available chopsticks and wait a few minutes to try again). But doing so could lead
Some philosophers, or all philosophers, starve to death (everyone quickly catches their left chopsticks and then waits for their right chopsticks to become available without putting down their left chopsticks). This last case, when everyone has someone else's needs
Resources, and waiting for the resources that others are holding, if you share resources until you get the other resources that you need but don't have, if you share resources until you get the other resources that you need but are not, then you will have a deadlock.
When a thread holds a lock forever, and other threads try to acquire the lock, they will always be blocked. When a thread Thread1 possession of lock A, wants to obtain the lock B, but while the thread Thread2 holds the B lock and tries to acquire a lock, two threads will wait forever.
This situation is the simplest form of deadlock.
Examples are the following code:
public class Deadlock {
private static Object Locka = new Object ();
private static Object lockb = new Object ();
public static void Main (string[] args) {
New deadlock (). Deadlock ();
}
private void Deadlock () {
Thread thread1 = new Thread (new Runnable () {
public void Run () {
Synchronized (Locka) {
try {
System.out.println (Thread.CurrentThread (). GetName () + "gets a lock ing. ");
Thread.Sleep (500);
System.out.println (Thread.CurrentThread (). GetName () + "sleep 500ms");
catch (Exception e) {
E.printstacktrace ();
}
System.out.println (Thread.CurrentThread (). GetName () + "Need B lock ... ");
Synchronized (LOCKB) {
System.out.println (Thread.CurrentThread (). GetName () + "B Lock get Success");
}
}
}
}, "Thread1");
Thread thread2 = new Thread (new Runnable () {
public void Run () {
Synchronized (LOCKB) {
try {
System.out.println (Thread.CurrentThread (). GetName () + "get B-lock ing." ");
Thread.Sleep (500);
System.out.println (Thread.CurrentThread (). GetName () + "sleep 500ms");
catch (Exception e) {
E.printstacktrace ();
}
System.out.println (Thread.CurrentThread (). GetName () + "Need a lock ... ");
Synchronized (Locka) {
System.out.println (Thread.CurrentThread (). GetName () + "a lock to obtain success");
}
}
}
}, "Thread2");
Thread1.start ();
Thread2.start ();
}
}
The results of the operation are shown below:
As a result, the two threads were locked into a deadlock, and the reason for the deadlock was that two threads tried to get multiple identical locks in different order. If the order in which the locks are requested is the same,
There will be no cyclic lock-dependent phenomenon (you wait for me to put the lock, I wait for you to put the lock), it will not produce a deadlock. If you can guarantee that each thread of lock A and lock B is requested at the same time, it is in the order from lock A to lock B, so there is no deadlock.
If all threads acquire a lock in a common fixed order, the program will not have a lock-order deadlock problem.
Under what circumstances would a deadlock occur?
1. Lock nesting is prone to deadlock. Workaround: When acquiring a lock, see if there is a nesting. Try not to use lock nesting, if you have to use the lock nesting, you need to specify the order of the lock, because the order of the parameters is beyond our control, in order to solve this problem, we must specify the order of the lock, and throughout the application,
Access to locks must always be in accordance with this established order.
The root cause of the deadlock in the example above is that the order of the acquisition is disorderly, beyond our control. The ideal scenario for this example is to pull out the business logic and put the lock code in a public way so that the two threads acquire the lock
is obtained from my public method, when the THREAD1 thread enters the public method, acquires a lock, and the Thread2 comes in again, but a lock has been acquired by the THREAD1 thread, Thread1 then acquires the lock B,THREAD2 thread can no longer acquire the lock a, Let alone to get the lock B, so there is a certain order.
The above examples are transformed as follows:
public class Deadlock {
private static Object Locka = new Object ();
private static Object lockb = new Object ();
public static void Main (string[] args) {
New deadlock (). Deadlock ();
}
private void Deadlock () {
Thread thread1 = new Thread (new Runnable () {
public void Run () {
Getlock ();
}
}, "Thread1");
Thread thread2 = new Thread (new Runnable () {
public void Run () {
Getlock ();
}
}, "Thread2");
Thread1.start ();
Thread2.start ();
}
public void Getlock () {
Synchronized (Locka) {
try {
System.out.println (Thread.CurrentThread (). GetName () + "gets a lock ing. ");
Thread.Sleep (500);
System.out.println (Thread.CurrentThread (). GetName () + "sleep 500ms");
catch (Exception e) {
E.printstacktrace ();
}
System.out.println (Thread.CurrentThread (). GetName () + "Need B lock ... ");
Synchronized (LOCKB) {
System.out.println (Thread.CurrentThread (). GetName () + "B Lock get Success");
}
}
}
}
The results of the operation are as follows:
You can see that pulling out the business logic, putting the code to get the lock in a public way, and getting the lock must always be in this order.
2. Introduce explicit lock timeout mechanism to avoid deadlock
The time-out mechanism is the technique of monitoring deadlocks and recovering from deadlocks by using the timed trylock characteristics of each explicit lock class to replace the temporal mechanism. In the mechanism of internal locking, as long as the lock is not obtained, it is always kept waiting, and
The locks shown allow you to define the time of the timeout, and the Trylock will return failure after the specified time after the lock has not been acquired. By using timeouts, although this time is much longer than you might expect to get, you can still be able to restart after the accident
Gain control over it. When attempting to acquire a timed lock, you do not need to know why. It may be that a deadlock occurs, a thread may incorrectly enter an infinite loop while holding a lock, or it may take more time to perform some activity than you
Expectations are much slower. But at least you have a chance to understand that your attempt has failed, documenting the useful information in this attempt, and starting over again, which is much more elegant than shutting down the entire thread.
Even if the timing lock is not applied to the entire system, using it to obtain multiple locks can be effective in dealing with deadlocks. If a request to acquire a lock times out, you can release the lock and back, wait for a while and try again, which is likely to eliminate the condition of the deadlock.
and sequential program recovery. (This technique works only when two locks are obtained at the same time; If multiple locks are requested in nested methods, you cannot simply release the outer lock, although you know that you already hold the lock)
An explicit lock Lock,lock is an interface that defines the operations of a number of abstractions. Unlike internal locking mechanisms, lock provides unconditional, polling, timed, and interruptible lock acquisition operations, and all locking and unlocking methods are explicit.
The implementation of the lock must provide the semantics for reporting the same memory visibility as the internal lock. But the semantic of the lock, scheduling algorithm, order guarantee, performance characteristics these can be different.
Lock interface Source code is as follows:
Public interface Lock {
Lock
void Lock ();
interruptible lock, intended thread waiting state, that is, a thread has acquired the lock, B thread is again
Fetch, but a thread notifies B that it intends to wait for the B thread.