Get multiple locks in the overall fixed order to avoid dead locks

zhaozj2021-02-08  381

When two or more threads are waiting to be blocked each other, a deadlock occurs. For example, the first thread is blocked by the second thread, which is waiting for a resource held by the second thread. The second thread does not release this resource before getting a resource held by the first thread. Since the first thread does not release its own resources before getting the resource held by the second thread, and the second thread will not release it before getting a resource held by the first thread. The resource held, so the two threads are deadly locked.

When writing multithreaded code, the deadlock is one of the most difficult problems. Because the deadlock may happen in the most unexpected place, find and correct it costs and laborious. For example, try to consider the code that locks multiple objects below.

Public int sumarrays (int [] a1, int [] a2)

{

INT value = 0;

INT size = a1.length;

IF (size == a2.length) {

Synchronized (a1) {// 1

Synchronized (A2) {// 2

For (int i = 0; i

Value = a1 [i] a2 [i];

}

}

}

Return Value;

}

This code is correctly locked the two array objects before requesting two array objects in an operation. It is short, written, is also suitable for the task to be executed; but unfortunately, it has a potential issue. This question is that it buried a deadlock, unless you call the same object in different threads to call this method. To see a potential deadlock, consider the following event sequences:

Create two array objects, Arraya, and Arrayb. Thread 1 calls Sumarrays method with the following calls: Sumarrays (arraya, arrayb); thread 2 calls Sumarrays method with the following call: Sumarrays (arrayb, arraya); thread 1 start executing the SUMARRAYS method and getting it at / / 1 The lock of the parameter A1 is for this call, it is the lock of the Arraya object. Then at // 2, the thread 1 is first robbed before the lock of Arrayb. Thread 2 starts executing the SUMARRAYS method and obtains the lock of the parameter A1 at / / 1. For this call, it is the lock of the Arrayb object. The thread 2 is then attempted to obtain the lock of the parameter A2 at // 2, which is the lock of the Arraya object. Since this lock is currently held by thread 1, thread 2 is blocked. Thread 1 starts executing and attempting to obtain the lock of the parameter A2, which is the lock of the Arrayb object. Since this lock is currently held by thread 2, thread 1 is blocked. Now the two threads are deadly locked.

One way to avoid this problem is to get the code in a fixed overall order. In this example, if the thread 1 and thread 2 call the SumarRays method in the same order, a deadlock will not occur. However, this technical requirement, programmers for multi-threaded code need to be extravagantly. The application of this technology seems to be unrealistic before you encounter this deadlock and have to debug.

Alternatively, you can also embed the interior of the object. This allows the code to query it ready to get the lock object to determine the correct locked order. As long as all objects that are about to lock, all objects are supported by locking order representations, and the codes that get locks follow this strategy, this potential deadlock can be avoided.

The disadvantage of embedding the lock order in the object is that this implementation will increase the memory requirements and runtime costs. In addition, in the above example, this technique needs to have a package object in the array to store the lock sequence information. For example, try to consider the following code, it is modified by the previous example, in which the lock sequence technology is implemented: Class Arraywithlockorder

{

Private static long num_locks = 0;

PRIVATE long LOCK_ORDER;

Private int [] Arr;

Public arraywithlockorder (int [] a)

{

Arr = a;

Synchronized (arraywithlockorder.class) {

Num_locks ; // Lock number plus 1.

Lock_order = num_locks; / / Set the unique Lock_Order for this object instance.

}

}

Public long lockorder ()

{

Return Lock_Order;

}

Public int [] array ()

{

Return ARR;

}

}

Class Someclass Implements Runnable

{

Public Int Sumarrays (ArraywithlockOrder A1,

Arraywithlockorder A2)

{

INT value = 0;

ArraywithLockorder first = a1; // Reserved one of the array references

Arraywithlockorder last = a2; // local copy.

INT size = a1.Array (). Length;

IF (size == a2.Array (). Length)

{

IF (a1.lockorder ()> a2.lockorder ()) // Determines the lock of the object

{// order.

FigSt = a2;

Last = a1;

}

Synchronized (first) {// locked the object in the correct order.

Synchronized (last) {

int [] arr1 == a1.Array ();

int [] arr2 == a2.Array ();

For (int i = 0; i

Value = arr1 [i] arr2 [i];

}

}

}

Return Value;

}

Public void run () {

// ...

}

}

In the first example, the ArrayWithlockorder class is provided as a package of array. Each of the new objects are created, which adds the Static Num_locks variable 1. A separate Lock_Order instance variable is set to the current value of the NUM_LOCKS STATIC variable. This ensures that the Lock_Order variable has a unique value for each object of this class. The Lock_Order instance variable acts as a lock sequence indicator for this object relative to other objects of the class.

Note that the Static Num_locks variable is operated in the SYNCHRONIZED statement. This is a must, because each instance of the object shares the STATIC variable of the object. Therefore, when two threads create an object of the ArrayWithLockorder class, if the code of the STATIC NUM_LOCKS variable is not synchronized, the variable may be destroyed. Synchronous processing for this code ensures that each object of the ArraywithLockorder class has a unique value. In addition, the SUMARRAYS method is also updated to include code that determines the correct lock order. Each object will be queried to obtain its lock order before requesting the lock. The number is smaller is first locked. This code can be guaranteed, regardless of whether each object is transmitted sequentially, they are always locked in the same order.

The Static Num_locks domain and the LOCK_ORDER domain are implemented as a long type. The long data type is implemented as a 64-bit symbolic binary complement integer. This means that the value of Num_locks and Lock_Order will start again after creating 9, 223, 372, 036, 854, 775, and 807 objects. You may not reach this limit, but this is possible under appropriate conditions.

Realizing the embedded lock order requires more work, use more memory, and will extend the execution time. However, if you have these types of deadlocks in your code, you may find it to do this. If you are unable to withstand additional memory and execution overhead, or you cannot accept the possibility of re-starting Num_locks or Lock_Order domains, you should carefully consider the predefined order of the lock object.

转载请注明原文地址:https://www.9cbs.com/read-829.html

New Post(0)