13 минут на общение и отправку общего компонента синхронизации в пакете и написание собственного компонента синхронизации.

предисловие

В прошлой статье я разобрался с AQS (AbstractQueuedSynchronizer) на уровне исходного кода за 10 минут и сказал, что большинство компонентов синхронизации в параллельном пакете JUC используют AQS для достижения

В этой статье реализуется компонент синхронизации через сам AQS и рассказывается об общих компонентах синхронизации в параллельном пакете JUC на уровне исходного кода.

Для этой статьи необходимы предварительные знания AQS. Если вы не знаете AQS, вы можете прочитать статью ~

Чтение этой статьи займет около 13 минут.

Пользовательские компоненты синхронизации

Чтобы облегчить понимание других компонентов синхронизации, давайте сначала воспользуемся AQS для реализации часто используемой реентерабельной блокировки.

Поток метода шаблона AQS фиксирован, нам в основном нужно только реализовать его методы, пытаясь получить состояние синхронизации и попытаться освободить состояние синхронизации.

Прежде всего, мы сначала оговариваем, что реализуемая реентерабельная блокировка является эксклюзивной.

Оговаривается, что состояние синхронизации вначале равно 0, а когда поток успешно получает блокировку, состояние синхронизации равно 1, а при повторном входе потока состояние синхронизации накапливается.

Предусмотрено, что при освобождении состояния синхронизации каждый раз вычитается одно состояние синхронизации.Только когда состояние синхронизации снижается до 0, происходит реальное освобождение исключительной блокировки.

Мы используем внутренний класс Sync для наследования AQS и переписываем его, tryAcquireчтобы попытаться получить состояние синхронизации, tryReleaseпопытаться освободить состояние синхронизации и isHeldExclusivelyоценить, удерживает ли текущий поток состояние синхронизации (этот метод будет использоваться при ожидании и уведомлении).

	static class Sync extends AbstractQueuedSynchronizer {
        /**
         * 判断当前线程是否持有同步状态
         *
         * @return
         */
        @Override
        protected boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
    }

В состоянии синхронизации

  1. Сначала определите, существует ли состояние синхронизации (то есть, равно ли состояние синхронизации 0), если состояние синхронизации существует, используйте CAS для его получения (0->1), и если это удалось, установите текущий поток как поток, который получает состояние синхронизации
  2. Если состояние синхронизации отсутствует (то есть состояние синхронизации не равно 0), проверьте, является ли поток, получивший состояние синхронизации, текущим потоком. Если это текущий поток, это означает, что на этот раз это повторный вход, и число повторного входа накапливается
  3. Другие ситуации указывают на то, что статус синхронизации не получен, возвращайте false и следуйте процессу AQS (постройте узлы для присоединения к AQS).
		/**
         * 尝试获取同步状态
         *
         * @param arg 获取同步状态的数量
         * @return
         */
        @Override
        protected boolean tryAcquire(int arg) {
            //1.获取同步状态
            int state = getState();
            //2.如果有同步状态则CAS替换 0->1
            if (state == 0) {
                if (compareAndSetState(state, 1)) {
                    //替换成功 说明获取到同步状态 设置当前获取同步状态线程
                    setExclusiveOwnerThread(Thread.currentThread());
                    return true;
                }
            } else if (getExclusiveOwnerThread() == Thread.currentThread()) {
                //3.没有同步状态  查看获取同步资源的线程是否为当前线程  可重入  累加重入次数
                setState(state + arg);
                return true;
            }

            //其他情况就是没获取到同步状态
            return false;
        }

в состоянии синхронизации выпуска

Только когда состояние синхронизации меняется на 0, оно действительно освобождается, в противном случае это количество вычетов повторного входа.

        /**
         * 尝试释放同步状态
         *
         * @param arg 释放同步状态的数量
         * @return
         */
        @Override
        protected boolean tryRelease(int arg) {
            //目标状态
            int targetState = getState() - arg;

            //真正释放锁
            if (targetState == 0) {
                setExclusiveOwnerThread(null);
                setState(targetState);
                return true;
            }

            //其他情况 扣减状态
            setState(targetState);
            return false;
        }

После использования внутреннего класса для реализации метода AQS мы реализуем интерфейс блокировки в классе пользовательского компонента синхронизации и используем внутренний класс для реализации метода AQS для реализации метода интерфейса блокировки.

Установите состояние синхронизации, которое должно быть получено и освобождено, равным 1, а метод, соответствующий прерыванию ответа и тайм-ауту, может использовать соответствующий метод в AQS.

public class MySynchronizedComponent implements Lock {

    public MySynchronizedComponent() {
        sync = new Sync();
    }

    private Sync sync;

    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        return sync.new ConditionObject();
    }

}

Фактически, нам нужно всего лишь реализовать метод попытки получения и освобождения состояния синхронизации для завершения наших собственных компонентов синхронизации.В этом преимущество использования AQS.

Кейс с кодом можно получить на складе git и разместить в конце этой статьи.

Реентерантлокок

ReentrantLock — это реентерабельная блокировка, предоставляемая в пакете concurrent. Помимо реализации функции синхронизации, она также может реагировать на прерывания, таймауты и реализовывать справедливые блокировки. Базовый уровень также реализуется через AQS

Функция ReentrantLock аналогична функции Synchroned, повторяющейся эксклюзивной блокировки, используемой для обеспечения синхронных операций в параллельных сценариях.

При его использовании необходимо отображать блокировку и разблокировку.Общий формат следующий:

reentrantLock.lock();
try{
    //....
}finally {
    reentrantLock.unlock();
}

Наконец, сначала разблокируйте, и блокировка должна быть помещена в самый внешний слой блока try и убедитесь, что между блокировкой и блоком try не будет создано никаких исключений.

Блокировка не помещается в попытку, поскольку реализация блокировки неизвестна и может вызвать непроверяемое исключение. Когда во время блокировки генерируется исключение, последующая разблокировка блокаfinally также вызовет исключение недопустимого монитора, что приведет к перекрытию.

Если между блокировкой и блоком try выдается исключение, разблокировка не может быть выполнена.

Помимо основных функций синхронизации, ReentrantLock также предоставляет API для реагирования на прерывания и таймауты. Студенты могут просматривать их конфиденциально.

Студентам, знакомым с реализацией ReentrantLock, возможно, знаком приведенный выше код пользовательского компонента синхронизации. Фактически он написан со ссылкой на нечестную блокировку ReentrantLock.

ReentrantLock использует внутренний класс Sync для наследования AQS, а также внутренние классы NonfairSync и FairSync для наследования Sync для достижения несправедливого и справедливого получения статуса синхронизации.

изображение.png

Недобросовестные блокировки пытаются получить состояние синхронизации. Процесс аналогичен, поэтому я не буду его описывать слишком подробно.

		final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

Так как же добиться справедливой блокировки для достижения состояния синхронизации?

Фактически, студенты, прочитавшие предыдущую статью AQS, будут знать это, как я сказал в предыдущей статье.

Вам нужно только добавить условие, прежде чем пытаться получить состояние синхронизации: есть ли в очереди задача-предшественник (то есть поставлена ​​ли в очередь FIFO для попадания в очередь)

Справедливая блокировка также реализована таким образом, предварительное условиеhasQueuedPredecessors

		protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

Повторный входЧтениеЗаписьБлокировка

Функция и реализация

Основанный на функции ReentrantLock, ReentrantReadWriteLock обеспечивает функцию чтения и записи блокировок, что повышает степень детализации блокировок.

В некоторых сценариях, которые читают больше и пишут меньше, разрешено одновременное чтение и разрешено получение нескольких потоков.Фактически, рассматривается общий тип AQS, и блокировка чтения также является общей.

В сценарии чтения и чтения все блокировки чтения/общие блокировки не будут блокироваться.

В сценариях чтения и записи, чтения и записи, записи и записи все будет заблокировано.

Например, если вы хотите получить блокировку записи, вам нужно дождаться разблокировки блокировки чтения и блокировки записи; если вы хотите получить блокировку чтения, вам нужно дождаться разблокировки блокировки записи.

ReentrantReadWriteLock добавляется на основе ReentrantLock ReadLockи WriteLockиспользуется как блокировка чтения и блокировка записи соответственно.

изображение.png

Фактически блокировки чтения являются общими блокировками, а блокировки записи — эксклюзивными блокировками.При реализации методов блокировки и разблокировки можно использовать состояния синхронизации совместного и эксклюзивного получения и освобождения соответственно.

На момент постройки в блокировке чтения-записи фактически используется один и тот же AQS.

        public ReentrantReadWriteLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
            readerLock = new ReadLock(this);
            writerLock = new WriteLock(this);
        }

		//读锁构造
		protected ReadLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

		//写锁构造
		protected WriteLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

То есть состояние синхронизации будет совместно использоваться блокировками чтения и записи, так как же им просматривать/изменять свою часть состояния синхронизации?

При блокировке чтения-записи состояние синхронизации делится на два: высокое 16-битное состояние синхронизации — блокировка чтения, а низкое 16-битное состояние синхронизации — блокировка записи.

изображение.png

Когда поток получает блокировку записи, статус записи равен +1. Поскольку статус записи низкий, он эквивалентен статусу синхронизации +1.

Когда поток получает блокировку чтения, статус чтения + 1, поскольку статус чтения находится на высоком уровне, он эквивалентен статусу синхронизации + (1<<16)

получение блокировки записи

Приобретение синхронных блокировок записи sync.tryAcquireможет быть честным или несправедливым, по сути, это эксклюзивное приобретение.

protected final boolean tryAcquire(int acquires) {
  
    Thread current = Thread.currentThread();
    //得到同步状态c
    int c = getState();
    //得到写状态(同步状态低16位 与上 全1)
    int w = exclusiveCount(c);
    if (c != 0) {
        //同步状态不为0,写状态为0,说明读状态不为0,读锁已经被获取,此时获取写锁失败
        //同步状态不为0,写状态也不为0,查看当前线程是否是获取写锁的线程,不是的话获取写锁失败
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        
        //只有当前线程获取过写锁才能进入这里
        
        //如果原来的写状态+这次重入的写状态 超过了 同步状态的0~15位 则抛出异常
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        
        //设置同步状态 因为写状态在低16位所以不用左移 (重入累加)
        setState(c + acquires);
        return true;
    }
    
    //同步状态为0 无锁时 
    //writerShouldBlock在非公平锁下返回false 在公平锁下查看是否有前驱任务
    //如果CAS失败则返回false
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    
    //CAS成功则 设置当前线程为获得独占锁(写锁)的线程
    setExclusiveOwnerThread(current);
    return true;
}

Проверьте исходный код, чтобы узнать:

  1. Когда есть блокировка (состояние синхронизации не 0), если есть только блокировка чтения (без блокировки записи), то произойдет непосредственный сбой; если есть только блокировка записи, проверьте, является ли текущий поток тем потоком, который получает блокировку записи (ситуация повторного входа)
  2. Если блокировки нет, выполните CAS для получения блокировки записи. Если это удастся, он установит поток для получения блокировки записи. Если это не удастся, он вернет

Согласно анализу исходного кода, мы можем знать, что блокировка записи допускает повторный вход , и при получении блокировки записи, если есть блокировка чтения, она будет заблокирована.

снятие блокировки записи

Снятие блокировки записи реализовано sync.tryReleaseв

protected final boolean tryRelease(int releases) {
    //判断当前线程是不是获取写(独占)锁的线程
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    
    //新状态
    int nextc = getState() - releases;
    //如果新状态低16位为0(没有写锁)就设置获取写锁的线程为空,然后设置同步状态,再返回
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        setExclusiveOwnerThread(null);
    
    setState(nextc);
    return free;
}

Релиз на самом деле аналогичен.Настоящим релизом является только когда статус записи равен 0. В остальных случаях вычитается количество повторных входов.

получение блокировки чтения

Приобретение блокировки чтения также является совместным приобретением.

		protected final int tryAcquireShared(int unused) {
            
            Thread current = Thread.currentThread();
            //同步状态
            int c = getState();
            
            //exclusiveCount 为获取写锁状态 低16位全与1
            //如果有写锁 并且 获取写锁的线程不是当前线程 则失败(说明允许同一线程获取写锁再获取读锁)
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            
            //获取读状态  (同步状态右移16位)
            int r = sharedCount(c);
            //读没被阻塞 没超过最大值 且CAS成功 记录信息 返回成功
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                }
                return 1;
            }
            return fullTryAcquireShared(current);
        }

При блокировке чтения одному и тому же потоку разрешено получить блокировку записи, а затем получить блокировку чтения.

В некоторых сценариях вам необходимо сначала записать данные, а затем прочитать данные, например:

  1. получить блокировку записи
  2. записать данные
  3. снять блокировку записи
  4. использовать (читать) данные

Это приведет к тому, что другие потоки получат блокировку записи после снятия блокировки записи, что приведет к некорректному чтению на четвертом шаге.

Правильное использование должно заключаться в получении блокировки чтения перед снятием блокировки записи:

  1. получить блокировку записи
  2. записать данные
  3. получить блокировку чтения
  4. снять блокировку записи
  5. читать данные

Таким образом, когда другие потоки получают блокировки записи, они будут заблокированы, поскольку все они являются блокировками чтения, а другие потоки не будут блокироваться, когда им потребуется чтение.

В сценарии с большим количеством операций чтения и меньшим количеством записей степень детализации блокировок чтения и записи более точная, операции чтения и записи не блокируются, а производительность параллелизма выше.

количество сигнала

Функция

Семафоры используются для контроля количества потоков, одновременно обращающихся к ресурсам.

Когда поток обращается к ресурсу, ему необходимо получить семафор перед доступом к нему. После доступа семафор освобождается. Семафор позволяет N потокам получить его одновременно.

Ниже приведен контроль того, что только 2 потока могут получить семафор одновременно.

		//初始化信号量
        Semaphore semaphore = new Semaphore(2);
        
        //每次只有两个线程能够获取到信号量执行
        ExecutorService executor =  Executors.newFixedThreadPool(4);
        for (int i = 0; i < 10; i++) {
            executor.execute(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"获得资源");

                    //执行任务
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    System.out.println(Thread.currentThread().getName()+"释放资源======");
                    semaphore.release();
                }
            });
        }

        executor.shutdown();

выполнить

Студенты, знакомые с AQS, должны быть в состоянии догадаться, что семафор на самом деле реализуется путем совместного использования.

При построении семафора указывается количество инициализированных семафоров, которое фактически предназначено для инициализации состояния синхронизации. Например, установка 2 семафоров означает установку состояния синхронизации на 2; вы также можете установить справедливость и несправедливость в конструкции

При получении семафора используйте общий метод реагирования на прерывания для выполнения в несправедливых условиях.nonfairTryAcquireShared

		final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                //获取同步状态
                int available = getState();
                //目标同步状态 
                int remaining = available - acquires;
                //没有信号量 或 CAS成功 都会返回目标同步状态 为负数时获取失败
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

При получении фактически вычитается семафор, который необходимо получить. Несколько потоков могут получить семафор одновременно. Используйте повторную попытку CAS+failure, чтобы обеспечить атомарность до тех пор, пока семафор не исчезнет или CAS не завершится успешно.

Когда семафор освобождается, освобожденный семафор фактически добавляется. Несколько потоков могут освободить семафор одновременно, поэтому используйте повторную попытку сбоя CAS+, чтобы обеспечить атомарность при выпуске.

		protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }

Обратный отсчетЗащелка

CountDownLatch эквивалентен счетчику, количество счетчиков задается при построении

Функция

Вызывающий countDownметод уменьшит количество

При вызове awaitметода, если еще есть количество, которое не было вычтено, он будет заблокирован до тех пор, пока количество не будет вычтено.

Когда поток выполняет N задач или когда задачу выполняют несколько потоков, вы можете использовать CountDownLatch, если хотите дождаться их завершения, прежде чем переходить к следующему шагу.

//初始化10
CountDownLatch countDownLatch = new CountDownLatch(10);
//固定线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 1; i <= 10; i++) {
    final int index = i;
    executor.execute(() -> {
        System.out.println(Thread.currentThread() + "处理任务" + index);
        
        //执行任务...
        
        //数量-1
        countDownLatch.countDown();
    });
}


//计数量为0时才可以继续执行
countDownLatch.await();
System.out.println("处理完任务");

executor.shutdown();

выполнить

Фактически его реализация аналогична реализации семафора, и она также осуществляется через общий доступ.

Когда вы устанавливаете начальное значение в конструкторе, вы фактически устанавливаете состояние синхронизации.

При выполнении countDownсуммы вычета это фактически вычет в состоянии синхронизации.Поскольку несколько потоков могут выполняться одновременно, используйте повторную попытку сбоя CAS+, чтобы гарантировать успешность вычета состояния синхронизации.

protected boolean tryReleaseShared(int releases) {
    // Decrement count; signal when transition to zero
    for (;;) {
        int c = getState();
        if (c == 0)
            return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}

При выполнении awaitфактически необходимо определить, равно ли состояние синхронизации 0, в противном случае это означает, что некоторые потоки не завершили выполнение задачи, блокируются и ждут.

protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}

ЦиклическийБарьер

Циклический барьер — это перерабатываемый барьер, его часто используют для сравнения с защелкой обратного отсчета.

Это похоже на барьер, позволяющий потокам сталкиваться с барьерами после выполнения задач до тех пор, пока все потоки не завершат свои задачи (все не достигнут барьера), и его можно использовать повторно.

		CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {
            System.out.println("所有线程到达屏障后,优先执行构造规定的runnable");
        });

        Thread t1 = new Thread(() -> {
            //执行任务
            task(cyclicBarrier);
        }, "t1");

        Thread t2 = new Thread(() -> {
            //执行任务
            task(cyclicBarrier);
        }, "t2");

        Thread t3 = new Thread(() -> {
            //执行任务
            task(cyclicBarrier);
        }, "t3");

        t1.start();
        t2.start();
        t3.start();

В методе задачи ожидание будет заблокировано до тех пор, пока все потоки не достигнут барьера.

private static void task(CyclicBarrier cyclicBarrier) {
    System.out.println(Thread.currentThread() + "执行任务...");

    try {
        TimeUnit.SECONDS.sleep(1);

        cyclicBarrier.await();
        System.out.println("所有线程都执行完, " + Thread.currentThread() + "走出屏障");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }
}

Циклический барьер будет записывать, сколько потоков необходимо для достижения барьера, и обеспечивать повторное использование через поколения.

Используйте реентерабельную блокировку для блокировки и разблокировки в ожидании. Всякий раз, когда поток достигает барьера (при выполнении ожидания), он уменьшается на единицу. Если он не равен 0, он блокируется. Когда он уменьшается до 0, это означает, что все потоки достигли барьер и разбудить другие потоки.и обновить новое поколение

Обмен

Exchanger используется для совместной работы между потоками и может использоваться для обмена переменными.

Exchanger<String> exchanger = new Exchanger();

new Thread(() -> {
    String A = "A";
    try {
        //B
        System.out.println(exchanger.exchange(A));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();

String B = "B";
try {
    //A
    String A = exchanger.exchange(B);
    System.out.println("A=" + A + " B=" + B);
} catch (InterruptedException e) {
    e.printStackTrace();
}

Когда поток сначала выполняет обмен, он будет ждать выполнения другого потока, а когда другой поток выполняет обмен, он пробуждает ожидающий поток.

Подведем итог

В этой статье основное внимание уделяется принципу предварительного знания AQS для реализации пользовательских компонентов синхронизации, а также объясняются функции и принципы компонентов синхронизации, обычно используемых в параллельных пакетах.

После наследования AQS вам нужно только реализовать такие методы, как попытка получить и освободить состояние синхронизации, чтобы настроить компонент синхронизации.

ReentrantLock — это эксклюзивная повторная блокировка, реализованная AQS, и состояние синхронизации начального значения равно 0; при получении блокировки, если блокировки нет, попробуйте самоприращение CAS, и блокировка будет успешно получена; если есть блокировка, она считается, что поток, получивший блокировку, является. Если это не текущий поток, это означает, что количество повторных блокировок может быть увеличено автоматически; при снятии блокировки из-за реентерабельного отношения только самоуменьшение до 0 является реальное открытие замка

ReentrantLock также предоставляет другие функции, такие как прерывание ответа, тайм-аут и справедливая блокировка.Реализация справедливой блокировки требует только добавления предпосылки получения блокировок: организация очереди FIFO в AQS, узел-предшественник является головным узлом.

ReentrantReadWriteLock обеспечивает общие блокировки чтения и монопольные блокировки записи, что делает состояние блокировки более детальным и разделяет 16 бит состояния синхронизации на состояния чтения и записи. Производительность параллелизма будет выше в сценариях с большим количеством операций чтения и меньшим количеством операций записи; блокировка, если есть блокировка чтения, она блокируется, если есть блокировка записи, она проверяет, является ли она реентерабельной; при получении блокировки чтения ее можно получить без блокировки записи, и если блокировка записи является текущая тема, ее также можно приобрести

Семафор используется для управления доступом потока к ресурсам, инициализации количества пользовательских семафоров, получения семафора сначала, когда поток обращается к ресурсу, а затем доступа к ресурсу после получения семафора; это реализуется путем совместного использования, поскольку несколько потоков могут получить и выпуск одновременно. Семафор должен использовать повторную попытку сбоя CAS+, чтобы обеспечить атомарность при реализации.

CountDownLatch используется для подсчета. Его можно использовать для одного потока для выполнения N задач или для нескольких потоков для выполнения одной задачи. Когда задача выполняется, используйте обратный отсчет для вычета состояния синхронизации. При выполнении метода await , пока состояние синхронизации не равно 0, поток будет блокироваться до тех пор, пока все задачи не будут выполнены (состояние синхронизации будет вычтено)

CyclicBarrier — это барьер, который можно использовать повторно. Он используется для ожидания, пока другие потоки достигнут барьера, прежде чем продолжить выполнение после достижения барьера несколькими потоками. Это реализуется с помощью повторной блокировки и генерации. При вызове await он самоуменьшается. Когда счетчик равен 0, это означает, что все потоки достигли барьера, пробуждают другие заблокированные потоки

Exchange используется для совместной работы между потоками и может обмениваться переменными между потоками.

Наконец (не занимайтесь блудством просто так, попрошайничайте три раза одним кликом~)

Эта статья включена в колонку от точки к линии, от линии к поверхности и в простых терминах строит систему знаний по параллельному программированию на Java . Заинтересованные студенты могут продолжать обращать внимание.

Примечания и примеры из этой статьи включены в gitee-StudyJava и github-StudyJava . Заинтересованные студенты могут продолжать обращать внимание в разделе stat~

Адрес дела:

Gitee-JavaConcurrentProgramming/src/main/java/C_AQSComponent

Github-JavaConcurrentProgramming/src/main/java/C_AQSComponent

Если у вас есть какие-либо вопросы, вы можете задать их в комментариях. Если вы считаете, что Цай Цай хорошо написан, вы можете поставить лайк, подписаться на него и добавить в закладки для поддержки~

Обратите внимание на Цай Цай, поделитесь больше галантерейными товарами, официальный аккаунт: Частная кухня Цай Цая

Эта статья опубликована OpenWrite, многопостовой платформой для ведения блогов !

Третьекурсник средней школы написал веб-версию Windows 12 Deepin - официально дебютировала IDE, . действительно независимые исследования и разработки» известная как « «Отец Хунмэна» Ван Чэнлу. : Система версии Hongmeng для ПК будет запущена в следующем году, а Wenxin будет открыта для всего общества.Официально выпущено 3.2.0 Green Language V1.0 Официально выпущено
{{o.name}}
{{м.имя}}

Supongo que te gusta

Origin my.oschina.net/u/6903207/blog/10108124
Recomendado
Clasificación