Notify, Notibleall разница (продукт-потребительский случай)

Java

В этой статье имитируются примеры производитель-потребитель с помощью wait(), notify(), notifyAll() и объясняется, почему при использовании notify() возникает взаимоблокировка.

1. Пример кода

1.1 Производители

package com.example.hxk.thread.demo;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author Smith 2019/3/21
 */
public class Producer implements Runnable{

    List<Integer> cache;

    public void put() throws InterruptedException {
        synchronized (cache) {
            while (cache.size() == 1) {
                try {
                    cache.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }

            TimeUnit.SECONDS.sleep(1);

            cache.add(1);
            System.out.println(Thread.currentThread().getName() + "生产者生产了一条。");
            cache.notify();
        }
    }

    public Producer(List<Integer> cache) {
        this.cache = cache;
    }

    @Override
    public void run() {
        while (true) {
            try {
                put();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

1.2 Потребители

package com.example.hxk.thread.demo;

import java.util.List;

/**
 * @author Smith 2019/3/21
 */
public class Customer implements Runnable {

    List<Integer> cache;

    public Customer(List<Integer> cache) {
        this.cache = cache;
    }

    private void custom() {
        synchronized (cache) {
            while (cache.size() == 0) {
                try {
                    cache.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            cache.remove(0);
            System.out.println(Thread.currentThread().getName() + "消费者消费了一条。");
            cache.notify();
        }
    }
    @Override
    public void run() {
        while (true) {
            custom();
        }
    }
}

1.3 Тестовый код

1.3.1 Один производитель и один потребитель

package com.example.hxk.thread.demo;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Smith 2019/3/21
 */
public class Test {

    public static void main(String[] args) {

        List<Integer> cache = new ArrayList<>();
        new Thread(new Producer(cache), "P1").start();
        new Thread(new Customer(cache), "C1").start();
    }
}

результат операции:

в заключении:

С уведомлением, одним производителем и одним потребителем производство и потребление работают без проблем.

1.3.2 Один производитель и два потребителя

package com.example.hxk.thread.demo;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Smith 2019/3/21
 */
public class Test {

    public static void main(String[] args) {

        List<Integer> cache = new ArrayList<>();

        new Thread(new Producer(cache), "P1").start();
        new Thread(new Customer(cache), "C1").start();
        new Thread(new Customer(cache), "C2").start();
    }
}

результат операции:

в заключении:

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

1.3.3 Producer и Customer в notify() заменены notifyAll()

Код больше не липкий.

результат операции

Программа снова работает в обычном режиме.

2. Разница между notify() и notifyAll

Каждый объект синхронизации имеет собственный пул блокировок и пул ожидания.

image.png

2.1 Пул блокировки и пул ожидания

  • Пул блокировки: предположим, что поток A уже владеет блокировкой объекта (примечание: не класса), а другие потоки хотят вызвать синхронизированный метод (или синхронизированный блок) этого объекта, потому что эти потоки входят в синхронизированный метод объекта. Владение блокировкой объекта должно быть получено заранее, но блокировка объекта в настоящее время принадлежит потоку A, поэтому эти потоки входят в пул блокировок объекта.
  • Пул ожидания: Предположим, что поток А вызывает метод ожидания () объекта, поток А снимает блокировку объекта (поскольку метод ожидания () должен появиться в синхронизированном, поэтому естественном при выполнении методе ожидания () до того, как поток А у него уже есть блокировка объекта), в то время как поток a входит в объект пула ожидания. Если другой поток вызывает тот же метод объекта notifyAll(), весь пул потоков, ожидающий блокировки объекта, войдет в пул объекта, готовый конкурировать за право владения блокировкой. Если другой поток вызывает тот же метод уведомления объекта (), то только один поток в пуле ожидания объекта (случайный) войдет в пул блокировки объекта.

2.2 Разница между notify() и notifyAll()

  • Когда поток вызывает метод wait(), он снимет блокировку и войдет в пул ожидания и не будет участвовать в соревновании за блокировку.
  • После вызова notify() поток из ожидающего пула (будет только один поток) войдет в пул блокировок объекта для участия в конкурсе блокировок.Если конкурс будет успешным, блокировка будет получена.Если конкурс не пройден , он останется в пуле блокировок и будет ждать следующего конфликта блокировок A.
  • После вызова notifyAll() все потоки в пуле ожидания войдут в пул блокировок объекта для участия в соревновании блокировок.

3. Пример анализа

Знание знания 2, вышеприведенные три примера легко объяснить.

1.3.1: Потому что есть только один продюсер и потребитель, всегда есть только один нить в ожидающем пуле, либо продюсер просыпается, что потребитель просыпается, или потребитель просыпается до продюсера, поэтому программа может работать успешно;

1.3.2: привести возможный пример

  1. Теперь есть три потока: производитель P1, потребитель C1 и C2. Когда они начинают работать, все три ждут конкуренции в пуле блокировок. Предположим, что C1 захватывает блокировку. Когда C1 выполняется, нет ресурсов для потребления и вызова ожидания () , снимите блокировку и войдите в пул ожидания.
  2. C2 захватывает блокировку и начинает потреблять Аналогично C2 также входит в пул ожидания. Теперь в пуле блокировок остался только P1.
  3. P1 получает блокировку и запускает производство.После завершения производства P1 начинает вызывать метод notify() для пробуждения C1 или C2 в пуле ожидания, а затем P1 вызывает метод wait() для снятия блокировки и входит в бассейн ожидания.
  4. Предполагая, что C1 пробужден, C1 входит в пул блокировок и получает блокировку.После потребления метод notify() пробуждает C2, C2 входит в пул блокировок, а C1 входит в пул ожидания.Теперь в пуле блокировок есть только C1 .
  5. C1 получил замок и обнаружил, что не было ресурсов для потребления. После ожидания () блокировка была выпущена, и он ввел в пул ожидания. Теперь все три потока ждут в пуле, и в бассейне блокировки нет нитей. привести к тупике!

1.3.3: После notifyAll() не возникает ситуации, когда пробуждаются только похожие потоки, поэтому в 1.3.2 не будет взаимоблокировок.

Цитировать

  • Автор: отправлено по электронной почте

Источник: ЦСДН оригинал:blog.csdn.net/emaied/art...

  • Автор: Пафф Райдер

Источник: Цзяньшу оригинал:woo woo Краткое описание.com/fear/45626 4 oh 0…