Введение в нить
что такое процесс
Короче,Процесс можно рассматривать как запущенную программу.. Это базовая единица системных программ, поэтому процесс является динамическим. Процесс — это работающая программа с определенной независимой функцией на определенном наборе данных. Процесс — это основная единица распределения ресурсов операционной системой.
что такое нить
Поток — это основная единица планирования операционной системы. Потоки также называют облегченными процессами.В процессе может быть создано несколько потоков.Эти потоки имеют свои собственные атрибуты, такие как счетчики, стеки и локальные переменные, и могут обращаться к переменным в общей памяти.
разница между процессом и потоком
- В программе есть хотя бы один процесс, а в процессе есть хотя бы один поток.
- Потоки разделены более точно, чем процессы, поэтому накладные расходы на выполнение меньше, а параллелизм выше.
- Процесс — это сущность с независимыми ресурсами; несколько потоков одного и того же процесса совместно используют ресурсы процесса.
Во-вторых, основное использование потока
нить (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.unpark
LockSupport.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