Juega con la concurrencia: dos excelentes herramientas para la programación concurrente: programa de tubería VS semáforo

Inserte la descripción de la imagen aquí

Soluciones de programación concurrente

A menudo nos encontramos con problemas de concurrencia en el proceso de desarrollo, cuando nos encontramos con problemas de concurrencia, generalmente los resolvemos mediante bloqueos. De hecho, hay dos esquemas de implementación para bloqueos como sigue

  1. señal
  2. Tubo

El proceso del tubo y el semáforo son equivalentes. El proceso del tubo se puede utilizar para realizar el semáforo, y el semáforo también se puede utilizar para realizar el proceso del tubo. El proceso del tubo es más amigable para los desarrolladores

señal

Inserte la descripción de la imagen aquí
El semáforo es un mecanismo de sincronización del sistema operativo, compuesto por una variable entera sem y dos operaciones atómicas

P (Prolaag, holandés intenta reducir): sem-1, si sem <0, ingresa a la cola de espera, de lo contrario continúa
V (Verhoog, holandés aumento): sem + 1, si sem <= 0, hay hilos en espera cola, despierta un hilo

La variable sem sólo puede ser modificada por operación PV cuando se completa la inicialización. La operación garantiza la atomicidad de la operación PV. P puede ser bloqueado, pero V no será bloqueado.

Código

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)
        }
    }
}

Los semáforos se pueden dividir en 2 categorías

  1. Semáforo binario: el número de recursos es 0 o 1
  2. Semáforo de recursos: el número de recursos es cualquier valor no negativo

El semáforo tiene las siguientes funciones

  1. Obtenga acceso mutuamente exclusivo a la sección crítica (área a la que solo puede acceder un hilo a la vez)
  2. Realice la sincronización condicional

Obtenga acceso mutuamente excluyente a la sección crítica

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

El valor inicial del semáforo debe ser 1, uso emparejado de operación fotovoltaica

Realice la sincronización condicional

Semaphore condition = new Semaphore(0);

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

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

El valor inicial del semáforo debe ser 0, ThreadA se bloqueará al realizar operaciones P, y ThreadB despertará el thread en espera ThreadA al realizar operaciones V

Utilice semáforos para implementar colas de bloqueo

Utilice un mutex de semáforo binario para lograr un acceso mutuamente exclusivo
Utilice dos semáforos de recursos notFul, notEmptyl para lograr la sincronización condicional

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;
    }
}

Tubo

Para resolver el problema de emparejar el semáforo en la operación PV de la región crítica, el proceso del tubo reúne las operaciones PV emparejadas para generar un nuevo método de programación concurrente, que es más consistente con el pensamiento orientado a objetos.

Inserte la descripción de la imagen aquí
El concepto de variables de condición se introduce en el proceso de seguimiento y cada variable compartida corresponde a una cola de espera.

Sincronizado se implementa en base al monitor, que contiene solo una cola de sincronización, y
también se implementa una cola de espera AQS basada en el monitor, que contiene solo una cola de sincronización, pero puede contener múltiples colas de espera.

Utilice monitores para implementar colas de bloqueo

Debido a que la idea de diseño de AQS en Java es la administración, utilizo las API relacionadas en AQS para realizar esta función.

Se puede lograr con una variable compartida y dos variables de condición del 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();
        }
    }
}

Blog de referencia

好 文
[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

Supongo que te gusta

Origin blog.csdn.net/zzti_erlie/article/details/114386966
Recomendado
Clasificación