Введение в нить
что такое процесс
Короче,Процесс можно рассматривать как запущенную программу.. Это базовая единица системных программ, поэтому процесс является динамическим. Процесс — это работающая программа с определенной независимой функцией на определенном наборе данных. Процесс — это основная единица распределения ресурсов операционной системой.
что такое нить
Поток — это основная единица планирования операционной системы. Потоки также называют облегченными процессами.В процессе может быть создано несколько потоков.Эти потоки имеют свои собственные атрибуты, такие как счетчики, стеки и локальные переменные, и могут обращаться к переменным в общей памяти.
разница между процессом и потоком
- В программе есть хотя бы один процесс, а в процессе есть хотя бы один поток.
- Потоки разделены более точно, чем процессы, поэтому накладные расходы на выполнение меньше, а параллелизм выше.
- Процесс — это сущность с независимыми ресурсами; несколько потоков одного и того же процесса совместно используют ресурсы процесса.
Во-вторых, основное использование потока
нить (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Шаги для создания потока для класса:
- определение
Threadподкласс класса и переопределитьrunметод.runТело метода представляет собой задачу, которую должен выполнить поток, поэтомуrunМетод называется исполнителем. - Создайте
ThreadСоздается экземпляр подкласса, то есть объект потока. - вызывающий объект потока
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Шаги для создания потока для интерфейса:
- определение
RunnableКласс реализации интерфейса и переопределение класса интерфейса.runметод. ДолженrunТело метода также является телом выполнения потока. - Создайте
Runnableреализует экземпляр класса и использует этот экземпляр какThreadцель для созданияThreadобъект,ThreadОбъект является реальным объектом потока. - вызывающий объект потока
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Шаги для создания потока для интерфейса:
- Создайте
CallableКласс реализации интерфейса и реализацияcallметод. ДолженcallМетод будет действовать как тело выполнения потока и иметь возвращаемое значение. - Создайте
CallableЭкземпляр реализующего класса, использующийFutureTaskкласс для обертыванияCallableобъект,FutureTaskобъект инкапсулируетCallableобъектcallВозвращаемое значение метода. - использовать
FutureTaskобъект какThreadЦель объекта создает и запускает новый поток. - перечислить
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и так далее для входа.метод входа Метод выхода Без установки параметра Timeout Object.waitметодObject.notify/Object.notifyAllБез установки параметра Timeout Thread.joinметодВызванный поток завершил выполнение LockSupport.parkметодLockSupport.unpark -
Время ожидания- Этот статус означает:Не нужно ждать явного пробуждения других потоков, они будут автоматически разбужены системой через определенный период времени..
метод входа Метод выхода Thread.sleepметодвремя вышло с установленным параметром Timeout Object.waitметодвремя вышло / Object.notify/Object.notifyAllс установленным параметром Timeout Thread.joinметодВремя вышло / вызванный поток завершил выполнение LockSupport.parkNanosметодLockSupport.unparkLockSupport.parkUntilметодLockSupport.unpark -
Прекращено- нить
runВыполнение метода завершается или завершается с исключениемrunметод. Это состояние означает: поток завершил свой жизненный цикл.
использованная литература
- «Практика параллельного программирования на Java»
- Искусство параллельного программирования на Java
- Взаимосвязь и различие между процессами и потоками
- Разница между методами yield и join в потоках Java
- Разница между методами sleep(), wait(), yield() и join()
- Параллельное программирование на Java: два способа взаимодействия между потоками: ожидание, уведомление, уведомление всех и условие
- Параллельное программирование на Java: Callable, Future и FutureTask
- StackOverflow VisualVM - Thread States
- Краткий обзор потоков демонов в Java
- Параллелизм Java
- Why must wait() always be in synchronized block