Основы работы с потоками в Java

Java

Введение в нить

что такое процесс

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

что такое нить

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

разница между процессом и потоком

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

Во-вторых, основное использование потока

нить (Thread) список основных методов:

метод описывать
run Исполнительный объект потока.
start Метод запуска потока.
currentThread Возвращает ссылку на текущий исполняемый объект потока.
setName Задайте имя потока.
getName Получите имя потока.
setPriority Установить приоритет потока. Диапазон приоритетов потоков в Java составляет [1, 10].Вообще говоря, потоки с высоким приоритетом будут иметь приоритет во время выполнения. в состоянии пройтиthread.setPriority(Thread.MAX_PRIORITY)Приоритет по умолчанию равен 5.
getPriority Получить приоритет потока.
setDaemon Установите поток как поток демона.
isDaemon Определяет, является ли поток потоком демона.
isAlive Определить, запущен ли поток.
interrupt Прерывает состояние выполнения другого потока.
interrupted Проверяет, был ли текущий поток прерван. Этот метод очищает прерванное состояние потока. Другими словами, если бы этот метод вызывался дважды подряд, второй вызов вернул бы false (если только текущий поток не будет снова прерван после того, как первый вызов очистит свое прерванное состояние и до того, как второй вызов проверит свое состояние).
join Поток может быть запущен принудительно.Во время принудительного запуска потока другие потоки не могут выполняться и должны дождаться завершения потока, прежде чем продолжить выполнение.
Thread.sleep статический метод. Засыпает текущий исполняемый поток.
Thread.yield статический метод. Приостановить выполнение текущего потока и позволить другим потокам выполняться.

создать тему

Существует три способа создания потока:

  • наследоватьThreadсвоего рода
  • выполнитьRunnableинтерфейс
  • выполнитьCallableинтерфейс

Наследовать от класса Thread

по наследствуThreadШаги для создания потока для класса:

  1. определениеThreadподкласс класса и переопределитьrunметод.runТело метода представляет собой задачу, которую должен выполнить поток, поэтомуrunМетод называется исполнителем.
  2. СоздайтеThreadСоздается экземпляр подкласса, то есть объект потока.
  3. вызывающий объект потокаstartметод запуска потока.
public class ThreadDemo {

    public static void main(String[] args) {
        // 实例化对象
        MyThread tA = new MyThread("Thread 线程-A");
        MyThread tB = new MyThread("Thread 线程-B");
        // 调用线程主体
        tA.start();
        tB.start();
    }

    static class MyThread extends Thread {

        private int ticket = 5;

        MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                ticket--;
            }
        }

    }

}

Реализовать интерфейс Runnable

выполнитьRunnableИнтерфейсы лучше, чем наследованиеThreadсвоего рода,так как:

  • Java не поддерживает множественное наследование, все классы могут наследовать только один родительский класс, но могут реализовывать несколько интерфейсов. Если унаследованоThreadКлассы не могут наследовать от других классов, что не способствует расширению.
  • От класса может требоваться только исполняемость, наследующий весьThreadНакладные расходы класса слишком высоки.

путем реализацииRunnableШаги для создания потока для интерфейса:

  1. определениеRunnableКласс реализации интерфейса и переопределение класса интерфейса.runметод. ДолженrunТело метода также является телом выполнения потока.
  2. СоздайтеRunnableреализует экземпляр класса и использует этот экземпляр какThreadцель для созданияThreadобъект,ThreadОбъект является реальным объектом потока.
  3. вызывающий объект потокаstartметод запуска потока.
public class RunnableDemo {

    public static void main(String[] args) {
        // 实例化对象
        Thread tA = new Thread(new MyThread(), "Runnable 线程-A");
        Thread tB = new Thread(new MyThread(), "Runnable 线程-B");
        // 调用线程主体
        tA.start();
        tB.start();
    }

    static class MyThread implements Runnable {

        private int ticket = 5;

        @Override
        public void run() {
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                ticket--;
            }
        }

    }

}

Реализовать вызываемый интерфейс

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

Для решения этой проблемы,После Java 1.5 предоставляетсяCallableинтерфейс иFutureИнтерфейсы, через которые можно вернуть результат выполнения после завершения выполнения потока.

путем реализацииCallableШаги для создания потока для интерфейса:

  1. СоздайтеCallableКласс реализации интерфейса и реализацияcallметод. ДолженcallМетод будет действовать как тело выполнения потока и иметь возвращаемое значение.
  2. СоздайтеCallableЭкземпляр реализующего класса, использующийFutureTaskкласс для обертыванияCallableобъект,FutureTaskобъект инкапсулируетCallableобъектcallВозвращаемое значение метода.
  3. использоватьFutureTaskобъект какThreadЦель объекта создает и запускает новый поток.
  4. перечислитьFutureTaskобъектgetметод для получения возвращаемого значения после завершения выполнения потока.
public class CallableDemo {

    public static void main(String[] args) {
        Callable<Long> callable = new MyThread();
        FutureTask<Long> future = new FutureTask<>(callable);
        new Thread(future, "Callable 线程").start();
        try {
            System.out.println("任务耗时:" + (future.get() / 1000000) + "毫秒");
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    static class MyThread implements Callable<Long> {

        private int ticket = 10000;

        @Override
        public Long call() {
            long begin = System.nanoTime();
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                ticket--;
            }

            long end = System.nanoTime();
            return (end - begin);
        }

    }

}

FAQ

startиrunв чем разница
  • runМетод — это тело выполнения потока.
  • startМетод запустит поток, а затем JVM позволит потоку выполниться.runметод.
можно вызвать напрямуюThreadКатегорияrunметод
  • Может. Но если вы напрямую позвонитеThreadизrunметод, он будет вести себя как обычный метод.
  • Чтобы выполнить наш код в новом потоке, мы должны использоватьThreadизstartметод.

нить сна

использоватьThread.sleepметод перевода текущего исполняемого потока в спящий режим.

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

Thread.sleepметод может броситьInterruptedException, так как исключения не могут распространяться обратно по потокамmain, поэтому он должен обрабатываться локально. Другие исключения, возникающие в потоке, также необходимо обрабатывать локально.

public class ThreadSleepDemo {

    public static void main(String[] args) {
        new Thread(new MyThread("线程A", 500)).start();
        new Thread(new MyThread("线程B", 1000)).start();
        new Thread(new MyThread("线程C", 1500)).start();
    }

    static class MyThread implements Runnable {

        /** 线程名称 */
        private String name;

        /** 休眠时间 */
        private int time;

        private MyThread(String name, int time) {
            this.name = name;
            this.time = time;
        }

        @Override
        public void run() {
            try {
                // 休眠指定的时间
                Thread.sleep(this.time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.name + "休眠" + this.time + "毫秒。");
        }

    }

}

Тема любезно предоставлена

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

Этот метод является всего лишь предложением для планировщика потоков, и это всего лишь предположение о том, что другие потоки с таким же приоритетом могут выполняться.

public class ThreadYieldDemo {

    public static void main(String[] args) {
        MyThread t = new MyThread();
        new Thread(t, "线程A").start();
        new Thread(t, "线程B").start();
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "运行,i = " + i);
                if (i == 2) {
                    System.out.print("线程礼让:");
                    Thread.yield();
                }
            }
        }
    }
}

убить нить

ThreadсерединаstopНесовершенный метод и устаревший.

использоватьThread.stopОстановка потока приводит к разблокировке всех заблокированных мониторов (из-за непроверенныхThreadDeathИсключения распространяются вверх по стеку как естественное следствие). Если какие-либо объекты, ранее защищенные этими мониторами, находятся в несогласованном состоянии, поврежденные объекты будут видны другим потокам, что может привести к произвольному поведению.Thread.stopМногие варианты использования должны быть заменены кодом, который изменяет только определенные переменные, чтобы указать, что целевой поток должен прекратить работу. Целевой поток должен периодически проверять эту переменную, и если переменная указывает, что он хочет прекратить выполнение, он должен вернуться из своего метода запуска упорядоченным образом. Если целевой поток ожидает в течение длительного времени (например, в переменной условия), следует использовать метод прерывания, чтобы прервать ожидание.

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

public class ThreadInterruptDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 实例化Runnable子类对象
        Thread t = new Thread(mt, "线程"); // 实例化Thread对象
        t.start(); // 启动线程
        try {
            Thread.sleep(2000); // 线程休眠2秒
        } catch (InterruptedException e) {
            System.out.println("3、休眠被终止");
        }
        t.interrupt(); // 中断线程执行
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println("1、进入run()方法");
            try {
                Thread.sleep(10000); // 线程休眠10秒
                System.out.println("2、已经完成了休眠");
            } catch (InterruptedException e) {
                System.out.println("3、休眠被终止");
                return; // 返回调用处
            }
            System.out.println("4、run()方法正常结束");
        }
    }
}

Если нитьrunметод выполняет бесконечный цикл и не выполняетсяsleepи т. д. будет бросатьInterruptedExceptionоперация, то вызывающий потокinterruptМетод не может привести к преждевременному завершению потока.

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

Есть два способа безопасно завершить поток:

  • определениеvolatileфлаг, вrunИспользуйте флаги в методах для управления завершением потока
  • использоватьinterruptМетоды иThread.interruptedметоды, используемые в сочетании с управлением завершением потока

Пример: использоватьvolatileфлаг завершения потока управления

public class ThreadStopDemo2 {

    public static void main(String[] args) throws Exception {
        MyTask task = new MyTask();
        Thread thread = new Thread(task, "MyTask");
        thread.start();
        TimeUnit.MILLISECONDS.sleep(50);
        task.cancel();
    }

    private static class MyTask implements Runnable {

        private volatile boolean flag = true;

        private volatile long count = 0L;

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 线程启动");
            while (flag) {
                System.out.println(count++);
            }
            System.out.println(Thread.currentThread().getName() + " 线程终止");
        }

        /**
         * 通过 volatile 标志位来控制线程终止
         */
        public void cancel() {
            flag = false;
        }

    }

}

Пример: использоватьinterruptМетоды иThread.interruptedметоды, используемые в сочетании с управлением завершением потока

public class ThreadStopDemo3 {

    public static void main(String[] args) throws Exception {
        MyTask task = new MyTask();
        Thread thread = new Thread(task, "MyTask");
        thread.start();
        TimeUnit.MILLISECONDS.sleep(50);
        thread.interrupt();
    }

    private static class MyTask implements Runnable {

        private volatile long count = 0L;

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 线程启动");
            // 通过 Thread.interrupted 和 interrupt 配合来控制线程终止
            while (!Thread.interrupted()) {
                System.out.println(count++);
            }
            System.out.println(Thread.currentThread().getName() + " 线程终止");
        }
    }
}

Нить демона

Что такое поток демона?

  • Поток демона — это поток, который выполняется в фоновом режиме и не препятствует завершению работы JVM. Когда все потоки, не являющиеся демонами, заканчиваются, программа завершается, и все потоки демонов уничтожаются.
  • В отличие от потока демона, он называется потоком пользователя, то есть потоком, не являющимся демоном.

Зачем нужен поток демона?

  • Поток демона имеет более низкий приоритет и используется для предоставления услуг другим объектам и потокам в системе. Типичным приложением является сборщик мусора.

Как использовать потоки демона?

  • можно использоватьisDaemonМетод определяет, является ли поток потоком демона.
  • можно использоватьsetDaemonМетод устанавливает поток как поток демона.
    • Работающий пользовательский поток не может быть установлен как поток демона, поэтомуsetDaemonДолжен бытьthread.startметод перед настройкой, иначе он выброситllegalThreadStateExceptionаномальный;
    • Дочерний поток, созданный потоком демона, по-прежнему является потоком демона.
    • Не думайте, что все приложения могут быть назначены потокам демона для выполнения служб, таких как операции чтения и записи или вычислительная логика.
public class ThreadDaemonDemo {

    public static void main(String[] args) {
        Thread t = new Thread(new MyThread(), "线程");
        t.setDaemon(true); // 此线程在后台运行
        System.out.println("线程 t 是否是守护进程:" + t.isDaemon());
        t.start(); // 启动线程
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            while (true) {
                System.out.println(Thread.currentThread().getName() + "在运行。");
            }
        }
    }
}

Справочное чтение:Краткий обзор потоков демонов в Java

FAQ

В чем разница между методами sleep, yield и join

  • yieldметод
    • yieldметод будетпусть нить изRunningгосударственная передачаRunnableгосударство.
    • когда звонятyieldметод, толькотого же или более высокого приоритета, что и текущий потокRunnableПоток состояния получит возможность выполнить.
  • sleepметод
    • sleepметод будетпусть нить изRunningгосударственная передачаWaitingгосударство.
    • sleepМетод должен указать время ожидания,По истечении времени ожидания JVM удалит поток изWaitingгосударственная передачаRunnableгосударство.
    • когда звонятsleepПосле метода,Темы любого приоритета могут получить шанс выполнить.
    • sleepМетод не снимает «флаг блокировки», т. е. если естьsynchronizedСинхронизированный блок, другие потоки по-прежнему не могут получить доступ к общим данным.
  • join
    • joinметод будетпусть нить изRunningгосударственная передачаWaitingгосударство.
    • когда звонятjoinПосле метода,Текущий поток должен ожидать вызоваjoinПоток метода может продолжать выполняться только после завершения потока..

Почему методы sleep и yield статичны

ThreadКатегорияsleepиyieldметод будет обрабатыватьRunningсостояние нити.

Так в др.RunningНет смысла выполнять эти два метода в потоке состояния. Вот почему эти методы являются статическими. Они могут работать в текущем выполняющемся потоке и не допускать ошибочного предположения программиста о том, что эти методы могут быть вызваны из других неработающих потоков.

Выполняются ли потоки Java строго в соответствии с приоритетом потока

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

Причина в том, что приоритет потока зависит от поддержки операционной системой, однако приоритеты потоков, поддерживаемые разными операционными системами, неодинаковы и не могут хорошо соответствовать приоритетам потоков в Java.

3. Межпоточная связь

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

wait/notify/notifyAll

  • wait - waitметод заставляет поток снять удерживаемую им блокировку объекта,пусть нить изRunningгосударственная передачаWaitingгосударство, и ждатьnotify / notifyAllпроснуться. Если блокировка не снята, другие потоки не могут войти в метод синхронизации или блок управления синхронизацией объекта, поэтому выполнение не может быть выполнено.notifyилиnotifyAllчтобы разбудить приостановленный поток, вызывая взаимоблокировку.
  • notify- проснутьсяWaitingСостояние потока и возможность его блокировки объекта, какой поток для пробуждения контролируется JVM.
  • notifyAll- проснуться всеWaitingсостояние потоков, а затем им нужно бороться за блокировки объектов.

Уведомление:

  • wait,notify,notifyAllобаObjectметоды в классе, вместоThread.
  • wait,notify,notifyAllможно использовать только вsynchronizedметод илиsynchronizedиспользуется в блоках кода, в противном случае он будет вызывать во время выполненияIllegalMonitorStateException.

Почемуwait,notify,notifyAllне определено вThreadсередина? Почемуwait,notify,notifyAllСотрудничатьsynchronizedиспользовать?

Во-первых, вам нужно понять несколько основных моментов знаний:

  • Каждый объект Java имеет соответствующиймонитор
  • Каждый монитор имеетблокировка объекта,Одиночередь ожидания,Одиночередь синхронизации

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

Почему эти методы не определены вThreadсередина?

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

Почемуwait,notify,notifyAllСотрудничатьsynchronizedиспользовать?

Если вы вызываете объектwaitметод, текущий поток должен владеть блокировкой объекта для этого объекта, поэтому вызовитеwaitметод должен быть вsynchronizedМетоды иsynchronizedв кодовом блоке.

Модель производитель-потребитель – этоwait,notify,notifyAllКлассический вариант использования:

public class ThreadWaitNotifyDemo02 {

    private static final int QUEUE_SIZE = 10;
    private static final PriorityQueue<Integer> queue = new PriorityQueue<>(QUEUE_SIZE);

    public static void main(String[] args) {
        new Producer("生产者A").start();
        new Producer("生产者B").start();
        new Consumer("消费者A").start();
        new Consumer("消费者B").start();
    }

    static class Consumer extends Thread {

        Consumer(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == 0) {
                        try {
                            System.out.println("队列空,等待数据");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notifyAll();
                        }
                    }
                    queue.poll(); // 每次移走队首元素
                    queue.notifyAll();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 从队列取走一个元素,队列当前有:" + queue.size() + "个元素");
                }
            }
        }
    }

    static class Producer extends Thread {

        Producer(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == QUEUE_SIZE) {
                        try {
                            System.out.println("队列满,等待有空余空间");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notifyAll();
                        }
                    }
                    queue.offer(1); // 每次插入一个元素
                    queue.notifyAll();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 向队列取中插入一个元素,队列当前有:" + queue.size() + "个元素");
                }
            }
        }
    }
}

join

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

public class ThreadJoinDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 实例化Runnable子类对象
        Thread t = new Thread(mt, "mythread"); // 实例化Thread对象
        t.start(); // 启动线程
        for (int i = 0; i < 50; i++) {
            if (i > 10) {
                try {
                    t.join(); // 线程强制运行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Main 线程运行 --> " + i);
        }
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println(Thread.currentThread().getName() + " 运行,i = " + i); // 取得当前线程的名字
            }
        }
    }
}

трубопровод

Разница между канальным потоком ввода/вывода и обычным файловым потоком ввода/вывода или сетевым потоком ввода/вывода заключается в том, что он в основном используется для передачи данных между потоками, а средой передачи является память. Поток ввода/вывода конвейера в основном включает следующие четыре конкретные реализации:PipedOutputStream,PipedInputStream,PipedReaderиPipedWriter, первые два ориентированы на байты, а последние два — на символы.

public class Piped {

    public static void main(String[] args) throws Exception {
        PipedWriter out = new PipedWriter();
        PipedReader in = new PipedReader();
        // 将输出流和输入流进行连接,否则在使用时会抛出IOException
        out.connect(in);
        Thread printThread = new Thread(new Print(in), "PrintThread");
        printThread.start();
        int receive = 0;
        try {
            while ((receive = System.in.read()) != -1) {
                out.write(receive);
            }
        } finally {
            out.close();
        }
    }

    static class Print implements Runnable {

        private PipedReader in;

        Print(PipedReader in) {
            this.in = in;
        }

        public void run() {
            int receive = 0;
            try {
                while ((receive = in.read()) != -1) {
                    System.out.print((char) receive);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4. Статус потока

java.lang.Thread.Stateопределено в6Существуют разные состояния потока, и в данный момент поток может находиться только в одном из этих состояний.

Ниже приводится описание каждого состояния и связь между состояниями:

  • Новый (Новый)- еще не звонилstartПоток метода находится в этом состоянии. Этот статус означает:Созданная тема не запущена.

  • Запускаемый- был вызванstartПоток метода находится в этом состоянии. Этот статус означает:Поток уже запущен в JVM. Но на уровне операционной системы он может находиться в рабочем состоянии или ожидать планирования ресурсов (например, ресурсов процессора) и переходить в рабочее состояние после завершения планирования ресурсов. Таким образом, работоспособность этого состояния означает, что его можно запустить, и то, будет ли оно запущено, зависит от планирования ресурсов базовой операционной системы.

  • Заблокировано- запрос на получение блокировки монитора для входаsynchronizedфункция или кодовый блок, но другие потоки уже заняли блокировку монитора, поэтому она заблокирована. Чтобы выйти из этого состояния, введитеRunnable, который требует, чтобы другие потоки сняли блокировку монитора. Этот статус означает:поток заблокирован.

  • Ожидающий- Этот статус означает:Потоки, ожидающие явного пробуждения другими потоками. Разница между блокировкой и ожиданием заключается в том, что блокировка является пассивной, она ожидает получения блокировки монитора. Пока активно ожидание, позвонивObject.waitи так далее для входа.

    метод входа Метод выхода
    Без установки параметра TimeoutObject.waitметод Object.notify / Object.notifyAll
    Без установки параметра TimeoutThread.joinметод Вызванный поток завершил выполнение
    LockSupport.parkметод LockSupport.unpark
  • Время ожидания- Этот статус означает:Не нужно ждать явного пробуждения других потоков, они будут автоматически разбужены системой через определенный период времени..

    метод входа Метод выхода
    Thread.sleepметод время вышло
    с установленным параметром TimeoutObject.waitметод время вышло /Object.notify / Object.notifyAll
    с установленным параметром TimeoutThread.joinметод Время вышло / вызванный поток завершил выполнение
    LockSupport.parkNanosметод LockSupport.unpark
    LockSupport.parkUntilметод LockSupport.unpark
  • Прекращено- нитьrunВыполнение метода завершается или завершается с исключениемrunметод. Это состояние означает: поток завершил свой жизненный цикл.

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