Java Concurrency-Thoughts and Summary about Management

Guan Cheng, the corresponding English is Monitor , many peers translate it into "monitor", which is a literal translation, and the field of operating systems generally translates into "Guan Cheng", which is a free translation, and I prefer the latter.

In the history of the development of management, three management models have appeared successively, namely: Hasen model, Hoare model and MESA model. The implementation of Java management refers to the widely used MESA model.

There are two implementations of monitor in Java: Synchronized and ReentrantLock

To understand a new thing, you must first clarify the problem it solves . There are two core problems in concurrent programming:

  • Mutually exclusive : Only one thread can access shared resources at the same time;
  • Synchronization : How to communicate and collaborate between threads.

Let's look at the first problem first, mutual exclusion:

Thanks to the programming features of java OOP (specifically encapsulation ), this problem is not difficult to deal with: just encapsulate the shared variable and its operations on the shared variable!

Think about how Collections.synchronizedList() in java implements a thread-safe container:

public E get(int index) {
    synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
    synchronized (mutex) {return list.set(index, element);}
}

Encapsulate a List (local variables, corresponding to the shared variables of the monitor model), and then lock get()&set() (corresponding to the operation of the shared variables in the monitor model).

In the same way, the following is the structure diagram of the MESA model to solve the mutual exclusion problem:

Monitor X{
    // 私有共享变量:队列
    var queue;
    // 入队
    func enq();
    // 出队
    func deq(); 
}

You will find that: the monitoring model and object-oriented are highly compatible

Focus on the second question, synchronization:

The specific implementation to solve the synchronization problem is the three commonly used methods:

  • wait()
  • notify()
  • notifyAll()

First, the flow chart of a monitor to solve synchronization:

The structure of the monitor here is: shared variable V, condition variable AB, waiting queue ab corresponding to each condition variable, entry waiting queue in, and various methods (wait(), notify(), notifyAll() are not marked here)

Let's simulate a scenario:

1) Thread T1 requests the lock for the first time, and all conditions are met at this time, so it obtains the shared variable V through path 1 (marked in red) in the figure;

2) When T1 has not released the lock, T2 requests to acquire the lock, which is the condition is not met (T1 is occupying V), so T2 enters the waiting queue ab of one of the condition variables of AB through path 2 (marked in red), and waits , The method of this process call is T2 calls wait() ;

3) T1 releases the lock, and the conditions of T2 are met. T2 enters the entry waiting queue in through path 3 (marked red), and is ready to acquire the lock again. The method of this process call is T1 calls notify() or notifyAll()

About notify() and notifyAll():

  • notify(): wake up any thread in the waiting queue of the condition variable;
  • notifyAll(): wake up all threads in the waiting queue of the condition variable;

"Effictive Java 3rd" item81: PREFER CONCURRENCY UTILITIES TO WAIT AND NOTIFY has this sentence:

It is Sometimes said that you should always use notifyAll.This is reasonable.conservative advice.

A common saying is that you should always use notifyAll(), which is a reasonable and conservative suggestion.

My understanding is:

If your lock has not been carefully refined (a lock corresponds to only one resource, it can also be understood that the threads requesting the lock need to meet the same conditions), you can only use notifyAll(). Because a lock corresponds to multiple resources, if you use notify() to randomly wake up a thread in the waiting queue, and the resource requested by this thread just has not been released (the condition variable is not satisfied), all the threads will eventually return to the condition variable waiting queue . Led to a condition variable waiting queue  forever, there will not be a thread notify (), so he formed a deadlock!

 

Thinking about whether ReentrantLock is a reinvention of the wheel:

Some people may ask: Since the implementation of monitor already has synchronized, why did ReentrantLock be released later? Reinvent the wheel?

The answer is definitely no!

Back to the top of the deadlock: Error Using notify () leads to a condition variable condition variable corresponding to the waiting queue threads never get resources so as to never wake up! We can't help thinking, if there is a way to let the thread that cannot acquire the lock for a long time release the resources by itself, the above deadlock can be avoided! You may have thought of the following code:

ReentrantLock lock = new ReentrantLock();
// 支持超时
lock.tryLock(1000, TimeUnit.MICROSECONDS);

Support timeout! Yes, this is one of the reasons for the emergence of ReentrantLock, as well as two other reasons:

// 支持中断
lock.lockInterruptibly();
// 支持非阻塞异步获取锁
lock.tryLock();

 

Guess you like

Origin blog.csdn.net/weixin_41346635/article/details/114171210