Pay attention to the public code byte to unlock more concurrent

In understanding the principle of Syncchronized, the implementation principle of Synchronize is introduced. Both the synchronization method and the synchronized code block, both ACC_SYNCHRONIZED, monitorenter and monitorexit are implemented based on Monitor. So this article introduces what Monitor is.

Managing: The process of managing shared variables and operations on them in such a way that they support concurrency. Java manages the member variables and methods of a class to make it thread-safe.

A program structure in which multiple worker threads formed by multiple subroutines (objects or modules) have exclusive access to shared resources. These shared resources are typically hardware devices or a group of variables. A managed program implements a subroutine that at most one thread is executing at a point in time. Compared to concurrent programming where mutually exclusive access is achieved by modifying data structures, managed implementations greatly simplify programming. A managed program provides a mechanism for a thread to temporarily waive mutex access, wait until certain conditions are met, and then regain execution rights to resume its mutex access.

MESA model

In the history of pipeline, there have been three different pipeline models, namely Hasen model, Hoare model and MESA model. Among them, the MESA model is widely used, and the Implementation of The Java pipeline refers to the MESA model. So today we’ll focus on the MESA model.

In the concurrency world, there are two core issues: mutual exclusion and synchronization. The pipeline is designed to solve these two problems.

  • Mutually exclusive: Only one thread is allowed to access the shared resource at a time.
  • Synchronization: How threads communicate and collaborate with each other.

Tube mutually exclusive and synchronous implementation

The idea is simple: it encapsulates shared variables and operations on shared variables. As shown in the figure below, pipeline A encapsulates the shared variable data and related operations enq() and deq(). Threads A and B can access the shared data variable only by calling the enq() and deq() provided by the pipe. Of course, the premise is that enq() and deq() are mutually exclusive, allowing only one thread to enter the pipe. Isn’t it very object oriented.

In the managed model, shared variables and operations on shared variables are encapsulated. The outermost box in the figure represents the meaning of encapsulation. There is only one entry on the top of the box, and there is another entry queue next to the entry. When multiple threads attempt to enter a pipe at the same time, only one thread is allowed to enter and the others wait in the entry wait queue. The procedure is similar to triage, where only one patient is allowed to see a doctor while the rest wait at the door.

The concept of condition variable is also introduced in the pipeline, and each condition variable should have A waiting queue. As shown in the figure below, condition variable A and condition variable B have their own waiting queues respectively.

A conditional notification is used to wake up threads on the waiting queue to compete for the resource lock.

We explain through a piece of code, to achieve a blocking queue, the queue is separately out of the queue and the queue, is to obtain the mutex, like the entry in the pipe.

  1. For enqueued operations, if the queue is full, you need to wait until the queue is not full, so this is usednotFull.await();.
  2. For outbound operations, if the queue is empty, you need to wait until the queue is not empty, so it is usednotEmpty.await();.
  3. If the queue is entered successfully, then the queue is not empty and the condition variable needs to be notified that the queue is not emptynotEmptyThe corresponding waiting queue.
  4. If the dequeuing is successful, the queue is dissatisfied and the condition variable is notified: Queue dissatisfiednotFullThe corresponding waiting queue.
public class BlockedQueue<T>{
  final Lock lock = new ReentrantLock();
  // Condition variable: queue dissatisfied
  final Condition notFull = lock.newCondition();
  // Condition variable: queue not empty
  final Condition notEmpty = lock.newCondition();
 
  / / team
  void enq(T x) {
    lock.lock();
    try {
      while(Queue full){// The queue is not satisfied
        notFull.await();
      }  
      // Omit the queue operation...
      // After joining the team, you can notify to leave the team
      notEmpty.signal();
    }finally{ lock.unlock(); }}/ / out of the team
  void deq(a){
    lock.lock();
    try {
      while(Queue empty){// The queue is not empty
        notEmpty.await();
      }
      // Omit the team operation...
      // If you leave the team, you can join the team
      notFull.signal();
    }finally{ lock.unlock(); }}}Copy the code

In this example code, we use Java and send the Lock and Condition inside the package. If you’re struggling, that’s ok. We’ll cover that later, but this example is just to show you how Condition variables and wait queues work. Note that await() has the same semantics as wait() we mentioned earlier; Signal () has the same semantics as notify() we mentioned earlier. Through conditional queue communication, the management program achieves synchronization, which provides basic support for concurrent programming in Java.

Pay attention to the public number code elder byte to get more concurrency principle

The giant shoulder

Geek time