Глубокое понимание API блокировки и пробуждения, предоставляемого Object.

Java

помещение

Некоторое время назад я потратил много времени на изучение синхронизатора в JUC.AbstractQueuedSynchronizerРеализация исходного кода в сочетании со статьей, о которой я читал давным-давноObjectРеализация механизма ожидания и пробуждения JVM предоставила и обнаружила, что между ними существует большая корреляция, поэтому я решил изучить ее повторно.ObjectМетоды блокировки и пробуждения, представленные в . Версия JDK, используемая в этой статье для чтения исходного кода библиотеки классов JDK, — JDK11, поскольку содержимое этой статьи может не подходить для других версий.

API блокировки и пробуждения, предоставляемый Object

java.lang.Objectкак базовый класс для всех непримитивных типов, т.java.lang.ObjectПодклассы имеют функции блокировки и пробуждения. Подробный анализ нижеObjectПредоставлены API блокировки и пробуждения.

блокировка ждать-ждать

ждать-wait()Метод обеспечивает функцию блокировки, разделенную на версии с тайм-аутом и постоянной блокировкой, фактически нижний слой предоставляет только метод JNI:

// 这个是底层提供的JNI方法,带超时的阻塞等待,响应中断,其他两个只是变体
public final native void wait(long timeoutMillis) throws InterruptedException;

// 变体方法1,永久阻塞,响应中断
public final void wait() throws InterruptedException {
    wait(0L);
}

// 变体方法2,带超时的阻塞,超时时间分两段:毫秒和纳秒,实际上纳秒大于0直接毫秒加1(这么暴力...),响应中断
public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
    if (timeoutMillis < 0) {
        throw new IllegalArgumentException("timeoutMillis value is negative");
    }
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }
    if (nanos > 0) {
        timeoutMillis++;
    }
    wait(timeoutMillis);
}

это только одинwait(long timeoutMillis)Метод представляет собой интерфейс JNI, а два других метода эквивалентны:

  • wait()Эквивалентноwait(0L).
  • wait(long timeoutMillis, int nanos)Эквивалентноwait(timeoutMillis + 1L).

из-заwait(long timeoutMillis, int nanos)Это метод с наиболее полными параметрами и очень длинными комментариями к API.Здесь мы напрямую переводим и извлекаем основные элементы в его комментариях:

  • 1. Текущий поток блокируется и ждет, пока он не будет разбужен.Обычно существует три случая пробуждения: вызывается уведомление(Все), поток прерывается или превышено указанное время блокировки, когда указан тайм-аут блокировки.
  • 2. Текущий поток должен получить блокировку монитора для этого объекта (monitor lock), то есть,Поток должен стать владельцем блокировки монитора для этого объекта перед вызовом метода ожидания блокировки..
  • 3. Позвонилwait()После метода текущий поток поместит себя в набор ожидания текущего объекта, а затем отпустит все заявки на синхронизацию для этого объекта (затем, чтобы отказаться от любых и всех заявок на синхронизацию для этого объекта), помните, что только текущий объект заявление о синхронизации наwait()метод будет выпущен позже.
  • 4.Warning: После пробуждения потока (notify()или прерывание) удаляется из набора ожидания и снова включается для планирования планировщиком потоков. Обычно пробужденный поток будет конкурировать с другими потоками за права синхронизации (блокировки) на объект после перезапуска потока.Контролировал объект (Восстановил контроль над объектом), он возвращает все синхронизированные объявления объекта в их предыдущее состояние, т.е. в вызывающееwait()метод (думаю, на самом деле, если быть точным, это вызовwait()состояние перед методом).
  • 5. Если какой-либо поток в нем вызываетwait()раньше или звонилwait()Затем метод находится в блокирующем состоянии ожидания, как только поток вызываетThread#interrupt(), поток прерывается и бросаетInterruptedExceptionИсключение, статус прерывания потока будет очищен.InterruptedExceptionИсключение откладывается до тех пор, пока оно не будет выброшено, когда вы упомянете в пункте 4, что «все его синхронизированные объявления для объекта восстанавливаются до их предыдущего состояния».

Также стоит отметить:

Поток должен быть владельцем блокировки монитора для этого объекта, чтобы он мог нормально вызываться.wait()метод серий, т.wait()Методы серии должны находиться внутри блока синхронизированного кода (synchronizedкодовый блок), иначе он выкинетIllegalMonitorStateExceptionаномальный, это новичок или не понимаюwait()Проблема, которую часто делают разработчики механизма.

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


final Object lock = new Object();

synchronized(lock){
    1、线程进入同步代码块,意味着获取对象监视器锁成功
    while(!condition){
        lock.wait();   2.线程调用wait()进行阻塞等待
        break;
    }
    3.线程从wait()的阻塞等待中被唤醒,恢复到第1步之后的同步状态
    4.继续执行后面的代码,直到离开同步代码块
}

пробуждение-уведомление

notify()Сигнатура метода выглядит следующим образом:

@HotSpotIntrinsicCandidate
public final native void notify();

Давайте как обычно переведем его комментарии к API:

  • 1. Разбудить заблокированный поток, ожидающий на этом объектном мониторе (если есть несколько заблокированных потоков), то, какой поток выбрать для пробуждения, является произвольным, в зависимости от конкретной реальности, поток, вызываяwait()способ блокировки на объектном мониторе.
  • 2. Пробужденный поток не будет продолжать выполняться немедленно, пока текущий поток (то есть текущий вызовnotify()поток метода) снимает блокировку объекта. Пробужденный поток будет конкурировать с другими потоками за синхронизацию объекта (другими словами, он может продолжить выполнение только в том случае, если получит контроль над синхронизацией объекта), и пробужденный поток не имеет надежных привилегий или недостатков, когда он становится следующим потоком. чтобы заблокировать объект.
  • 3. Этот метод может быть вызван только тогда, когда поток получает владельца монитора объекта, а именно: в синхронизированном методе, в синхронизированном блоке кода или в статическом синхронизированном методе. Иначе выкинетIllegalMonitorStateExceptionаномальный.

разбудить всех -notifyAll

notifyAll()Сигнатура метода выглядит следующим образом:

@HotSpotIntrinsicCandidate
public final native void notifyAll();

1. Разбудить все потоки, заблокированные в ожидании на этом объектном мониторе, поток, вызвавwait()способ блокировки на объектном мониторе.

описание других заметок иnotify()Метод аналогичен.

резюме

упоминается в информации, которую мы часто видимsynchronizedИспользование ключевого слова:

  • Обычный метод синхронизации, который синхронизирует или блокирует текущий экземпляр объекта.
  • Метод статической синхронизации, который синхронизирует или блокирует текущий объект экземпляра.Classобъект.
  • Синхронизированные блоки кода, синхронизируйте или заблокируйте экземпляр объекта внутри круглых скобок.

Для синхронизированных кодовых блоковsynchronizedАбстракция ключевых слов на уровень байт-кода заключается в том, что выполнение байт-кода в блоке синхронизированного кода выполняется вmonitorenterа такжеmonitorexitМежду командами:

synchronized(xxxx){

    ...coding block
}

↓↓↓↓↓↓↓↓↓↓

monitorenter;
...coding block - bytecode
monitorexit;

JVM должна гарантировать, что каждый вход монитора имеет соответствующий выход монитора. Любой объект имеет монитор (собственноObjectMonitor) связан с ним, и после удержания монитора он будет заблокирован. Когда поток выполняет команду monitorenter, он попытается получить право собственности на монитор, соответствующий объекту, то есть попытается получить блокировку объекта.

Для синхронизированных (статических) методовsynchronizedМетоды транслируются в обычные инструкции вызова и возврата метода, такие как:invokevirtualПодождите, на уровне байт-кода JVM нет специальных инструкций для реализацииsynchronizedмодифицированный метод, но вClassВ таблице методов файла методaccess_flagsв полеsynchronizedБит флага 1, указывающий, что метод является синхронным методом и использует объект, вызывающий метод, или метод, которому принадлежит метод.ClassПредставление внутреннего объекта в JVMKlassкак объект блокировки.

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

Повторить нижеНесколько вопросов, которые на первый взгляд кажутся необоснованными, но являются фактическими(На самом деле об этом упоминалось ранее):

  1. Войдите в веткуsynchronizedБлок метода или кода, эквивалентный успешному получению блокировки монитора, если он успешно вызывается в это время.wait()series, то он немедленно снимет блокировку монитора и добавит его в набор ожидания (Wait Set) для блокировки ожидания.
  2. Так как поток уже освободил блокировку монитора, то входит другой поток.synchronizedПосле метода или блока кода он может вызватьnotify(All)Метод пробуждает блокирующий поток в наборе ожидания, но эта операция пробуждения не является вызовом.notify(All)метод вступает в силу немедленно, но при завершении потокаsynchronizedОн вступает в силу только после метода или блока кода.
  3. отwait()Поток, который пробуждается во время процесса блокировки метода, будет конкурировать за контроль над целевым объектом монитора.Как только объект будет восстановлен, состояние синхронизации потока будет восстановлено для переходаsynchronizedСостояние метода или блока кода (т. е. состояние, когда блокировка монитора объекта успешно получена), при котором поток может продолжить выполнение.

Чтобы проверить эти три пункта, вы можете написать простое демо:

public class Lock {

    @Getter
    private final Object lock = new Object();
}

public class WaitMain {

    private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static void main(String[] args) throws Exception {
        final Lock lock = new Lock();
        new Thread(new  WaitRunnable(lock), "WaitThread-1").start();
        new Thread(new  WaitRunnable(lock), "WaitThread-2").start();
        Thread.sleep(50);
        new Thread(new  NotifyRunnable(lock), "NotifyThread").start();
        Thread.sleep(Integer.MAX_VALUE);
    }

    @RequiredArgsConstructor
    private static class WaitRunnable implements Runnable {

        private final Lock lock;

        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(String.format("[%s]-线程[%s]获取锁成功,准备执行wait方法", F.format(LocalDateTime.now()),
                        Thread.currentThread().getName()));
                while (true) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        //ignore
                    }
                    System.out.println(String.format("[%s]-线程[%s]从wait中唤醒,准备exit", F.format(LocalDateTime.now()),
                            Thread.currentThread().getName()));
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        //ignore
                    }
                    break;
                }
            }
        }
    }

    @RequiredArgsConstructor
    private static class NotifyRunnable implements Runnable {

        private final Lock lock;

        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(String.format("[%s]-线程[%s]获取锁成功,准备执行notifyAll方法", F.format(LocalDateTime.now()),
                        Thread.currentThread().getName()));
                lock.notifyAll();
                System.out.println(String.format("[%s]-线程[%s]先休眠3000ms", F.format(LocalDateTime.now()),
                        Thread.currentThread().getName()));
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    //ignore
                }
                System.out.println(String.format("[%s]-线程[%s]准备exit", F.format(LocalDateTime.now()),
                        Thread.currentThread().getName()));
            }
        }
    }
}

Результат выполнения в определенный момент таков:

[2019-04-27 23:28:17.617]-线程[WaitThread-1]获取锁成功,准备执行wait方法
[2019-04-27 23:28:17.631]-线程[WaitThread-2]获取锁成功,准备执行wait方法
[2019-04-27 23:28:17.657]-线程[NotifyThread]获取锁成功,准备执行notifyAll方法 <-------- 这一步执行完说明WaitThread已经释放了锁
[2019-04-27 23:28:17.657]-线程[NotifyThread]先休眠3000ms
[2019-04-27 23:28:20.658]-线程[NotifyThread]准备exit <------- 这一步后NotifyThread离开同步代码块
[2019-04-27 23:28:20.658]-线程[WaitThread-1]从wait中唤醒,准备exit <------- 这一步WaitThread-1解除阻塞
[2019-04-27 23:28:21.160]-线程[WaitThread-2]从wait中唤醒,准备exit <------- 这一步WaitThread-2解除阻塞,注意发生时间在WaitThread-1解除阻塞500ms之后,符合我们前面提到的第3点

если вместеwait()а такжеnotify()Псевдокод синхронизированного блока кода можно кратко резюмировать следующим образом:

final Object lock = new Object();

// 等待
synchronized(lock){
    1、线程进入同步代码块,意味着获取对象监视器锁成功
    while(!condition){
        lock.wait();   2.线程调用wait()进行阻塞等待
        break;
    }
    3.线程从wait()的阻塞等待中被唤醒,尝试恢复第1步之后的同步状态,并不会马上生效,直到notify被调用并且调用notify方法的线程已经释放锁,同时当前线程需要竞争成功
    4.继续执行后面的代码,直到离开同步代码块
}

// 唤醒
synchronized(lock){
    1、线程进入同步代码块,意味着获取对象监视器锁成功
    lock.notify();  2.唤醒其中一个在对象监视器上等待的线程
    3.准备推出同步代码块释放锁,只有释放锁之后第2步才会生效
}

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

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

j-u-c-o-w-n-1.png

  • Входной набор (фактическиObjectMonitorАтрибут _EntryList в ): Сохраняет потоки, ожидающие блокировки и находящиеся в заблокированном состоянии.
  • Ждать Установить (фактическиObjectMonitorСвойство _WaitSet): хранить в заблокированном состоянии ожидания потока.
  • Хозяин (на самом делеObjectMonitorАтрибут _owner в ): указывает на поток, который получает монитор объекта. Владелец может одновременно удерживать только один поток. Вообще говоря, это право управления монитором.

Пример использования

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

Пример ремонта туалета

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

// 厕所类
public class Toilet {
    // 厕所的锁
    private final Object lock = new Object();
    private boolean available;

    public Object getLock() {
        return lock;
    }

    public void setAvailable(boolean available) {
        this.available = available;
    }

    public boolean getAvailable() {
        return available;
    }
}

// 厕所维修工
@RequiredArgsConstructor
public class ToiletRepairer implements Runnable {

    private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
    private final Toilet toilet;

    @Override
    public void run() {
        synchronized (toilet.getLock()) {
            System.out.println(String.format("[%s]-厕所维修员得到了厕所的锁,维修厕所要用5000ms...", LocalDateTime.now().format(F)));
            try {
                Thread.sleep(5000);
            } catch (Exception e) {
                // ignore
            }
            toilet.setAvailable(true);
            toilet.getLock().notifyAll();
            System.out.println(String.format("[%s]-厕所维修员维修完毕...", LocalDateTime.now().format(F)));
        }
    }
}

//上厕所的任务
@RequiredArgsConstructor
public class ToiletTask implements Runnable {

    private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
    private final Toilet toilet;
    private final String name;
    private final Random random;

    @Override
    public void run() {
        synchronized (toilet.getLock()) {
            System.out.println(String.format("[%s]-%s得到了厕所的锁...", LocalDateTime.now().format(F), name));
            while (!toilet.getAvailable()) {
                try {
                    toilet.getLock().wait();
                } catch (InterruptedException e) {
                    //ignore
                }
                int time = random.nextInt(3) + 1;
                try {
                    // 模拟上厕所用时
                    TimeUnit.SECONDS.sleep(time);
                } catch (InterruptedException e) {
                    //ignore
                }
                System.out.println(String.format("[%s]-%s上厕所用了%s秒...", LocalDateTime.now().format(F), name, time));
            }
        }
    }
}

// 场景入口
public class Main {

    public static void main(String[] args) throws Exception {
        Toilet toilet = new Toilet();
        Random random = new Random();
        Thread toiletRepairer = new Thread(new ToiletRepairer(toilet), "ToiletRepairer");
        Thread thread1 = new Thread(new ToiletTask(toilet, "张三", random), "thread-1");
        Thread thread2 = new Thread(new ToiletTask(toilet, "李四", random), "thread-2");
        Thread thread3 = new Thread(new ToiletTask(toilet, "王五", random), "thread-3");
        thread1.start();
        thread2.start();
        thread3.start();
        Thread.sleep(50);
        toiletRepairer.start();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

Результат выполнения следующий:

[2019-04-29 01:07:25.914]-张三得到了厕所的锁...
[2019-04-29 01:07:25.931]-李四得到了厕所的锁...
[2019-04-29 01:07:25.931]-王五得到了厕所的锁...
[2019-04-29 01:07:25.951]-厕所维修员得到了厕所的锁,维修厕所要用5000ms...
[2019-04-29 01:07:30.951]-厕所维修员维修完毕...
[2019-04-29 01:07:32.952]-张三上厕所用了2秒...
[2019-04-29 01:07:35.952]-王五上厕所用了3秒...
[2019-04-29 01:07:37.953]-李四上厕所用了2秒...

реализация очереди блокировки

Реализуйте простую блокирующую очередь фиксированной емкости со следующим интерфейсом:

public interface BlockingQueue<T> {

    void put(T value) throws InterruptedException;

    T take() throws InterruptedException;
}

вput(T value)будет блокироваться до тех пор, пока в очереди не будет доступной емкости, в то время какtake()Метод блокируется до тех пор, пока элемент не будет помещен в очередь. Реализация выглядит следующим образом:

public class DefaultBlockingQueue<T> implements BlockingQueue<T> {

    private Object[] elements;
    private final Object notEmpty = new Object();
    private final Object notFull = new Object();
    private int count;
    private int takeIndex;
    private int putIndex;

    public DefaultBlockingQueue(int capacity) {
        this.elements = new Object[capacity];
    }

    @Override
    public void put(T value) throws InterruptedException {
        synchronized (notFull) {
            while (count == elements.length) {
                notFull.wait();
            }
        }
        final Object[] items = this.elements;
        items[putIndex] = value;
        if (++putIndex == items.length) {
            putIndex = 0;
        }
        count++;
        synchronized (notEmpty) {
            notEmpty.notify();
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public T take() throws InterruptedException {
        synchronized (notEmpty) {
            while (count == 0) {
                notEmpty.wait();
            }
        }
        final Object[] items = this.elements;
        T value = (T) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length) {
            takeIndex = 0;
        }
        count--;
        synchronized (notFull) {
            notFull.notify();
        }
        return value;
    }
}

Класс входов сцены:

public class Main {

    public static void main(String[] args) throws Exception {
        BlockingQueue<String> queue = new DefaultBlockingQueue<>(5);
        Runnable r = () -> {
            while (true) {
                try {
                    String take = queue.take();
                    System.out.println(String.format("线程%s消费消息-%s", Thread.currentThread().getName(), take));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        new Thread(r, "thread-1").start();
        new Thread(r, "thread-2").start();

        IntStream.range(0, 10).forEach(i -> {
            try {
                queue.put(String.valueOf(i));
            } catch (InterruptedException e) {
                //ignore
            }
        });

        Thread.sleep(Integer.MAX_VALUE);
    }
}

Результат выполнения следующий:

线程thread-1消费消息-0
线程thread-2消费消息-1
线程thread-1消费消息-2
线程thread-2消费消息-3
线程thread-1消费消息-4
线程thread-2消费消息-5
线程thread-1消费消息-6
线程thread-2消费消息-7
线程thread-1消费消息-8
线程thread-2消费消息-9

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

Реализация пула потоков

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

public interface ThreadPool {

    void execute(Runnable runnable);
}

Конкретная реализация выглядит следующим образом:

public class DefaultThreadPool implements ThreadPool {

    private final int capacity;
    private List<Worker> initWorkers;
    private Deque<Worker> availableWorkers;
    private Deque<Worker> busyWorkers;
    private final Object nextLock = new Object();

    public DefaultThreadPool(int capacity) {
        this.capacity = capacity;
        init(capacity);
    }

    private void init(int capacity) {
        initWorkers = new ArrayList<>(capacity);
        availableWorkers = new LinkedList<>();
        busyWorkers = new LinkedList<>();
        for (int i = 0; i < capacity; i++) {
            Worker worker = new Worker();
            worker.setName("Worker-" + (i + 1));
            worker.setDaemon(true);
            initWorkers.add(worker);
        }
        for (Worker w : initWorkers) {
            w.start();
            availableWorkers.add(w);
        }
    }

    @Override
    public void execute(Runnable runnable) {
        if (null == runnable) {
            return;
        }
        synchronized (nextLock) {
            while (availableWorkers.size() < 1) {
                try {
                    nextLock.wait(500);
                } catch (InterruptedException e) {
                    //ignore
                }
            }
            Worker worker = availableWorkers.removeFirst();
            busyWorkers.add(worker);
            worker.run(runnable);
            nextLock.notifyAll();
        }
    }

    private void makeAvailable(Worker worker) {
        synchronized (nextLock) {
            availableWorkers.add(worker);
            busyWorkers.remove(worker);
            nextLock.notifyAll();
        }
    }

    private class Worker extends Thread {

        private final Object lock = new Object();
        private Runnable runnable;
        private AtomicBoolean run = new AtomicBoolean(true);

        private void run(Runnable runnable) {
            synchronized (lock) {
                if (null != this.runnable) {
                    throw new IllegalStateException("Already running a Runnable!");
                }
                this.runnable = runnable;
                lock.notifyAll();
            }
        }

        @Override
        public void run() {
            boolean ran = false;
            while (run.get()) {
                try {
                    synchronized (lock) {
                        while (runnable == null && run.get()) {
                            lock.wait(500);
                        }

                        if (runnable != null) {
                            ran = true;
                            runnable.run();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    synchronized (lock) {
                        runnable = null;
                    }
                    if (ran) {
                        ran = false;
                        makeAvailable(this);
                    }
                }
            }
        }
    }
}

Запись класса сцены:

public class Main {

    private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static void main(String[] args) throws Exception{
        ThreadPool threadPool = new DefaultThreadPool(2);
        threadPool.execute(() -> {
            try {
                System.out.println(String.format("[%s]-任务一开始执行持续3秒...", LocalDateTime.now().format(F)));
                Thread.sleep(3000);
                System.out.println(String.format("[%s]-任务一执行结束...", LocalDateTime.now().format(F)));
            }catch (Exception e){
                //ignore
            }
        });
        threadPool.execute(() -> {
            try {
                System.out.println(String.format("[%s]-任务二开始执行持续4秒...", LocalDateTime.now().format(F)));
                Thread.sleep(4000);
                System.out.println(String.format("[%s]-任务二执行结束...", LocalDateTime.now().format(F)));
            }catch (Exception e){
                //ignore
            }
        });
        threadPool.execute(() -> {
            try {
                System.out.println(String.format("[%s]-任务三开始执行持续5秒...", LocalDateTime.now().format(F)));
                Thread.sleep(5000);
                System.out.println(String.format("[%s]-任务三执行结束...", LocalDateTime.now().format(F)));
            }catch (Exception e){
                //ignore
            }
        });
        Thread.sleep(Integer.MAX_VALUE);
    }
}

Результат выполнения следующий:

[2019-04-29 02:07:25.465]-任务二开始执行持续4秒...
[2019-04-29 02:07:25.465]-任务一开始执行持续3秒...
[2019-04-29 02:07:28.486]-任务一执行结束...
[2019-04-29 02:07:28.486]-任务三开始执行持续5秒...
[2019-04-29 02:07:29.486]-任务二执行结束...
[2019-04-29 02:07:33.487]-任务三执行结束...

резюме

Ввиду того, что автор плохо владеет языком C, здесь невозможно глубоко проанализировать реализацию исходного кода JVM, и мы можем только реорганизовать его на основе некоторых существующих данных и моего собственного понимания.ObjectПредоставьте эти знания о механизмах блокировки и пробуждения. Сочетая ранее исходный код синхронизатора JUC, я внезапно понял, что синхронизатор JUC использует язык Java только для блокировки и пробуждения языка C в исходной JVM на уровне структуры данных и алгоритма.ObjectПредоставленные методы JNI реализуются только один раз.

наконец,ObjectПредоставленный блокирующий механизм пробуждения с ожиданием реализован JVM (если вы хорошо знакомы с языком C, вы можете изучить его реализацию через исходный код JVM, который является черным ящиком для большинства разработчиков), если только версия JDK не слишком низкая и пакет JUC не был введен.В общем, не следует отдавать предпочтениеObject, вместо этого рассмотрите библиотеки в пакете JUC, разработанные специально для параллелизма.

Использованная литература:

Оригинальная ссылка

(Конец этой статьи c-7-d e-a-20190430)

Технический публичный аккаунт ("Throwable Digest"), который время от времени выкладывает оригинальные технические статьи автора (никогда не занимайтесь плагиатом и не перепечатывайте):