Play with concurrency: two great tools for concurrent programming: pipe program VS semaphore

Insert picture description here

Concurrent programming solution

We often encounter concurrency problems in the development process. When we encounter concurrency problems, we usually solve them through locks. In fact, there are two implementation schemes for locks as follows

  1. signal
  2. Tube

The tube process and the semaphore are equivalent. The tube process can be used to realize the semaphore, and the semaphore can also be used to realize the tube process. The tube process is more friendly to developers

signal

Insert picture description here
Semaphore is a synchronization mechanism of the operating system, composed of an integer variable sem and two atomic operations

P (Prolaag, Dutch try to reduce): sem-1, if sem<0, enter the waiting queue, otherwise continue
V (Verhoog, Dutch increase): sem+1, if sem<=0, there are threads in the waiting queue, Wake up a thread

The sem variable can only be modified by PV operation when the initialization is completed. The operation guarantees the atomicity of the PV operation. P may be blocked, but V will not be blocked.

Code

public class Semaphore {
    
    

    private int sem;
    private WaitQueue q;

    public void P() {
    
    
        sem--;
        if (sem < 0) {
    
    
            add this thread t to q;
            // 阻塞线程
            block(t);
        }
    }

    public void V() {
    
    
        sem++;
        if (sem <= 0) {
    
    
            remove a thread t from q;
            // 唤醒线程
            wakeup(t)
        }
    }
}

Semaphores can be divided into 2 categories

  1. Binary semaphore: the number of resources is 0 or 1
  2. Resource semaphore: the number of resources is any non-negative value

The semaphore has the following functions

  1. Realize mutually exclusive access to critical section (area that can only be accessed by one thread at a time)
  2. Realize conditional synchronization

Realize mutually exclusive access to the critical section

Semaphore mutex = new Semaphore(1);
mutex.P();
// do something
mutex.V();

The initial value of the semaphore must be 1, PV operation paired use

Realize conditional synchronization

Semaphore condition = new Semaphore(0);

//  ThreadA,进入等待队列中
condition.P();

// ThreadB,唤醒等待线程 ThreadA
condition.V();

The initial value of the semaphore must be 0, ThreadA will be blocked when performing P operations, and ThreadB will wake up the waiting thread ThreadA when performing V operations

Use semaphores to implement blocking queues

Use a binary semaphore mutex to achieve mutually exclusive access
Use two resource semaphores notFul, notEmptyl to achieve conditional synchronization

public class BlockingQueueUseSemaphore<T> {
    
    

    private final Object[] items;
    private Semaphore notFull;
    private Semaphore notEmpty;
    private Semaphore mutex;
    private int putIndex;
    private int takeIndex;

    public BlockingQueueUseSemaphore(int capacity) {
    
    
        this.items = new Object[capacity];
        notFull = new Semaphore(capacity);
        notEmpty = new Semaphore(0);
        mutex = new Semaphore(1);
    }

    public void enq(T x) throws InterruptedException {
    
    
        notFull.acquire();
        mutex.acquire();
        items[putIndex] = x;
        if (++putIndex == items.length) {
    
    
            putIndex = 0;
        }
        mutex.release();
        notEmpty.release();
    }

    public T deq() throws InterruptedException {
    
    
        notEmpty.acquire();
        mutex.acquire();
        T x = (T) items[takeIndex];
        if (++takeIndex == items.length) {
    
    
            takeIndex = 0;
        }
        mutex.release();
        notFull.release();
        return x;
    }
}

Tube

In order to solve the trouble of pairing the semaphore in the critical region PV operation, the tube process gathers the paired PV operations together to generate a new concurrent programming method, which is more consistent with the object-oriented thinking.

Insert picture description here
The concept of condition variables is introduced in the monitoring process, and each shared variable corresponds to a waiting queue

Synchronized is implemented based on the monitor, which contains only one synchronization queue, and a waiting queue
AQS is also implemented based on the monitor. It contains only one synchronization queue, but it can contain multiple waiting queues.

Use monitors to implement blocking queues

Because the design idea of ​​AQS in Java is management, I use the related APIs in AQS to realize this function

It can be achieved with one shared variable and two condition variables of the monitor

public class BlockingQueueUseMonitor<T> {
    
    

    private final Object[] items;
    private final Lock lock;
    private Condition notFull;
    private Condition notEmpty;
    private int count;
    private int putIndex;
    private int takeIndex;

    public BlockingQueueUseMonitor(int capacity) {
    
    
        this.items = new Object[capacity];
        lock = new ReentrantLock();
        notFull = lock.newCondition();
        notEmpty = lock.newCondition();
    }

    public void enq(T x) throws InterruptedException {
    
    
        lock.lock();
        try {
    
    
            while (count == items.length) {
    
    
                // 等待队列不满
                notFull.await();
            }
            items[putIndex] = x;
            if (++putIndex == items.length) {
    
    
                putIndex = 0;
            }
            count++;
            notEmpty.signal();
        } finally {
    
    
            lock.unlock();
        }
    }

    public T deq() throws InterruptedException {
    
    
        lock.lock();
        try {
    
    
            while (count == 0) {
    
    
            	 // 等待队列不空
                 notEmpty.await();
            }
            T x = (T) items[takeIndex];
            if (++takeIndex == items.length) {
    
    
                takeIndex = 0;
            }
            count--;
            notFull.signal();
            return x;
        } finally {
    
    
            lock.unlock();
        }
    }
}

Reference blog

好文
[1]https://www.cnblogs.com/binarylei/p/12544002.html#26-aqs-%E5%92%8C-synchronized-%E5%8E%9F%E7%90%86
[2]https://www.codenong.com/cs109504287/
管程
[3]https://time.geekbang.org/column/article/86089
信号量
[4]https://time.geekbang.org/column/article/88499

Guess you like

Origin blog.csdn.net/zzti_erlie/article/details/114386966