Synchronized

Consider a question: what is the problem that locks are designed to solve? What is the nature of locks? The essence of lock is a concept proposed to solve the problem of thread resource preemption in the use of shared resources by multiple threads.

The use of the lock

Public class Test {// Synchronize static void Test (){} public class Test {// synchronize static void Test (){} public class Test {// synchronize static void Test (){// Synchronize static void method(){// Object obj = new Object(); Void demo(){synchronized (obj){// critical section}}}Copy the code

Synchronized three locking methods

1. Modify instance method, function on the current instance lock, enter the synchronization code to obtain the current instance lock.

2. Modify static method, function and the current class object lock, enter the synchronization code to obtain the current class object lock.

3. Modify the code block to lock the given object before entering the synchronization code.

Lock upgrades favor locks -> Lightweight locks -> heavyweight locks

Biased locking

In most cases, locks are not contested by multiple threads and are always acquired multiple times by the same thread.

When a thread accesses a block of code with a synchronized lock, the object header stores the current thread ID, and the subsequent thread enters and exits the locked code quickly without the need to re-lock and release the lock. Instead, it directly compares whether the bias lock to the current thread is stored in the object header.

Biased locking is designed to minimize unnecessary lightweight lock execution paths in the absence of multithreaded contention. (The purpose of biased locking is to eliminate data synchronization without contention, and further improve the performance of the program)

Lightweight lock

If the bias lock is closed or the current object bias lock has been acquired by another thread, the lock will be upgraded to lightweight if a thread preempts the synchronous lock.

Heavyweight lock

When multiple threads compete for the same lock, the virtual machine blocks the thread that failed to lock and wakes up those threads when the target lock is released. Java threads are blocked and woken up by the operating system, OS pthread_mutex_lock(); Upgrade to a heavyweight lock, and the status value of the lock flag changes to “10”. At this time, the pointer to the heavyweight lock is stored in the Mark Word, and all the threads waiting for the lock will enter the blocking state.

conclusion

Biased lock only uses CAS to record the address of the current thread in the mark of the lock object in the first request. When the thread enters the synchronization code block again later, it does not need to preempt the lock, but can directly determine the thread ID. This applies to the situation that the lock will be preempt by the same thread for many times.

Lightweight locks use CAS to replace the marker field of the lock object with a pointer to the LockRecord in the current thread stack frame. This artifact stores the original marker field of the lock object, which is for multiple threads applying for a lock in different periods of time.

Heavyweight locks block and wake up the locked thread, and are used when multiple threads are competing for the same lock.

What is a deadlock? What are the four necessary conditions for a deadlock? What is a deadlock? A deadlock is an impasse (waiting on each other) caused by multiple processes competing for resources, and none of these processes can move forward without external action.

The cause?

  1. Competition for system resources. System resource competition leads to insufficient system resources and improper resource allocation, resulting in deadlocks.

  2. The process running sequence is not proper. When a process is running, the sequence of requesting and releasing resources is incorrect, resulting in a deadlock.

Four necessary conditions for deadlock

Mutual exclusion, hold and wait, non-preemption, circular wait.

The mutex

A resource can only be used by one process at a time, that is, a resource can only be used by one process at a time. If another process requests the resource, the requesting process can only wait.

Hold and wait for known possession of resources X and Y, thread T1, thread T2

Thread T1 has preempted resource X and wants to preempt resource Y, but resource Y has been preempted by thread T2. In this case, thread T1 is waiting for resource Y but does not release resource X.

How to break possession and wait

Method 1: All processes must request all the resources they need for the entire process at once before they start running.

Advantages: Simple, easy to implement and safe.

Disadvantages: A process cannot be started because a resource is not satisfied, and other resources that have been satisfied cannot be used, which seriously reduces resource utilization and wastes resources. The process often suffers from hunger.

Method 2: This method is an improvement on the first method, allowing the process to get only the resources it needs at the beginning of the run, and then start running, gradually releasing the allocated resources that have been used up during the run, and then requesting new resources. In this way, resource utilization will be improved and hunger will be reduced in the process.

Do not take

A resource acquired by a process cannot be seized by another process before it is fully used, that is, it can only be released by the process that acquired the resource itself (only on its own initiative).

How to break non-preemption

When a process that already holds some resources makes new resource requests and is not satisfied, it must release all resources it has held and reapply for them later when it needs to use them.

Loop waiting for

We know we have resources X and Y, thread T1, thread T2

T1 has resource X waiting to be released. T2 has resource Y waiting to be released

How do I break the circular wait

Each resource can be numbered. If a process owns the resource numbered I, it can only apply for the resource numbered greater than I next time