что такое поток демон java

Потоки-демоны в Java

Потоки-демоны работают в фоновом режиме вместе с программой, но не являются неотъемлемой частью программы. Если какой-либо процесс может выполняться на фоне работы основных потоков выполнения и его деятельность заключается в обслуживании основных потоков приложения, то такой процесс может быть запущен как поток-демон. С помощью метода setDaemon(boolean value), вызванного вновь созданным потоком до его запуска, можно определить поток-демон. Метод boolean isDaemon() позволяет определить, является ли указанный поток демоном или нет.

/* пример # 6 : запуск и выполнение потока-демона: DemoDaemonThread.java */

class T extends Thread <

sleep(10000); // заменить параметр на 1

System.out.println(«старт обычного потока»);

> catch (InterruptedException e) <

«завершение обычного потока»);

public class DemoDaemonThread <

public static void main(String[] args) <

«последний оператор main»);

В результате компиляции и запуска, возможно, будет выведено:

последний оператор main

старт обычного потока

завершение обычного потока

Поток-демон (из-за вызова метода sleep(10000)) не успел завершить выполнение своего кода до завершения основного потока приложения, связанного с методом main(). Базовое свойство потоков-демонов заключается в возможности основного потока приложения завершить выполнение потока-демона (в отличие от обычных потоков) с окончанием кода метода main(), не обращая внимания на то, что поток-демон еще работает. Если уменьшать время задержки потока-демона, то он может успеть завершить свое выполнение до окончания работы основного потока.

Источник

Демоническая нить в Java

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

Свойства:

Методы:

// Java-программа для демонстрации использования
// setDaemon () и метод isDaemon ().

public class DaemonThread extends Thread

public DaemonThread(String name)<

// Проверка, является ли поток Daemon или нет

System.out.println(getName() + » is Daemon thread» );

System.out.println(getName() + » is User thread» );

public static void main(String[] args)

DaemonThread t1 = new DaemonThread( «t1» );

DaemonThread t2 = new DaemonThread( «t2» );

DaemonThread t3 = new DaemonThread( «t3» );

// Установка пользовательского потока t1 в Daemon

// начинаем первые 2 темы

// Установка пользовательского потока t3 в Daemon

Исключения в ветке Daemon

// Java-программа для демонстрации использования
// исключение в потоке Daemon ()

public class DaemonThread extends Thread

System.out.println( «Thread name: » + Thread.currentThread().getName());

System.out.println( «Check if its DaemonThread: «

public static void main(String[] args)

DaemonThread t1 = new DaemonThread();

DaemonThread t2 = new DaemonThread();

// Исключение, так как поток уже запущен

Исключение во время выполнения:

Это ясно показывает, что мы не можем вызвать метод setDaemon () после запуска потока.

Daemon vs User Threads

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

Источник

Многопоточность в Java. Лекция 2: потоки, свойства потоков, блокировки

что такое поток демон java. articles java and dogs big 1600x960 1. что такое поток демон java фото. что такое поток демон java-articles java and dogs big 1600x960 1. картинка что такое поток демон java. картинка articles java and dogs big 1600x960 1. Потоки-демоны работают в фоновом режиме вместе с программой, но не являются неотъемлемой частью программы. Если какой-либо процесс может выполняться на фоне работы основных потоков выполнения и его деятельность заключается в обслуживании основных потоков приложения, то такой процесс может быть запущен как поток-демон. С помощью метода setDaemon(boolean value), вызванного вновь созданным потоком до его запуска, можно определить поток-демон. Метод boolean isDaemon() позволяет определить, является ли указанный поток демоном или нет.

Темную силу чувствую я.
Даешь парсек за три года.

Вводную статью о многопоточности в Java читайте здесь! В ее продолжении мы рассмотрим основы многопоточных программ: создание, запуск и свойства потока, синхронизацию потоков. Далее поговорим об использовании ключевого слова synchronized, volatile переменных и отношении happens-before.

2.1 Средства для работы с многопоточностью в Java и модели многопоточных программ

В первой версии Java инструментов для работы с многопоточностью было немного. Основные средства: класс Thread, интерфейс Runnable, ключевое слово synchronized и методы для синхронизации wait(), notify() и notifyAll() в классе Object. В версию Java 1.5 уже был включен пакет java.util.concurrent, в котором появилось много новых классов и интерфейсов. Также в версии Java 1.8 добавили класс CompletableFuture, который позволяет строить цепочки из асинхронных задач и комбинировать их.

Существуют несколько подходов (моделей) в многопоточном программировании:

Сейчас процессоры хорошо поддерживают концепцию потоков. Например, akka (фрэймворк для работы с многопоточностью, портированный на разные языки программирования: Java, Scala, C#) написан на основе потоков и блокировок.

Способы организации многопоточности в программах:

2.2 Свойства потоков, запуск потоков, присоединение других потоков

Все методы программы выполняются в каком-либо потоке. Поток, который вызывает метод main, является главным потоком приложения и имеет имя main.

В Java поток представлен классом Thread. Создать и запустить поток можно двумя способами:

1) Создать наследника от класса Thread и переопределить метод run().

Листинг 1:

public class MyThread extends Thread <
public void run() <
long sum = 0;
for (int i = 0; i
System.out.println(“Hello!”);
>

Thread t = new Thread(r);

Для запуска потока необходимо использовать метод Thread.start(). Если вызвать метод run(), то он выполнится в вызывающем потоке:

Листинг 3:

Thread t = new Thread(r);

t.run(); //код r выполняется в текущем потоке

t.start(); //код r выполняется в новом потоке

Не следует запускать поток из конструктора класса. Некоторые фреймворки, такие как Spring, создают динамические подклассы для поддержки перехвата методов. В конечном счете, мы получим два потока, запущенных из двух экземпляров.

Объект текущего потока можно получить, вызвав статический метод: Thread.currentThread().

Имена потокам можно задавать через метод setName() или через параметр конструктора. Рекомендуется давать потокам осмысленные имена, это пригодится при отладке. Не рекомендуется давать потокам одинаковые имена, хотя имена потоков не валидируются JVM.

Стандартный формат имен потоков, которые были созданы одиночно — thread-N, где N порядковый номер потока. Для пула потоков, стандартное наименование — pool-N-thread-M, где N обозначает последовательный номер пула (каждый раз, когда вы создаете новый пул, глобальный счетчик N увеличивается), а M — порядковый номер потока в пуле.

У потоков есть приоритет, который можно задать целым числом от 1 до 10. Чем больше число, тем выше приоритет потока. Поток main имеет приоритет 5. А приоритет новых потоков равен приоритету потока-родителя, его можно изменить при помощи метода setPriority(int). Поток с большим приоритетом будет иметь больше процессорного времени на выполнение. Если два потока имеют одинаковый приоритет, то решение о том, какой из них будет выполняться первым, зависит от алгоритма планировщика: (Round-Robin, First Come First Serve).

Есть несколько констант для приоритета потоков:

Листинг 4:

public class Main <
public static void main(String[] args) <
System.out.println(Thread.currentThread().getName());
Thread.currentThread().setPriority(8);
Thread thread = new Thread() <
public void run() <
Thread.currentThread().setName(«My name»);
System.out.println(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getPriority());
>
>;
thread.start();
>
>

В Java есть такое понятие, как поток-демон. Работа JVM заканчивается, когда закончил выполняться последний поток не-демон, несмотря на работающие потоки-демоны. Для работы с этим свойством существуют два метода: setDaemon() и isDaemon().

Класс ThreadGroup. Все потоки находятся в группах, представленных экземплярами класса ThreadGroup. Группа указывается при создании потока. Если группа не была указана, то поток помещается в ту же группу, в которой находится поток-родитель. Методы activeCount() и enumerate() возвращают, соответственно, количество и полный список всех активных потоков в группе.

Нестатический метод join() позволяет одному потоку дождаться выполнения другого. Если текущий поток t1 вызывает у другого потока t2h2t2.join(), то поток th2 останавливается до тех пор, пока поток t2 не завершит свою работу. Вызвать метод join() можно также и с аргументом, указывающим лимит времени ожидания (в миллисекундах или в миллисекундах с нано секундами). Если целевой поток t2 не закончит работу за указанный период времени, метод join() все равно вернет управление инициатору t1.

2.3 Остановка и прерывание потоков

Для остановки потока в Java версии 1 использовался метод stop(). Однако в версии Java 1.1 этот метод сделали deprecated, потому что использование метода stop() не гарантирует корректного завершения работы потока и стабильной работы программы в целом. Поэтому при написании программ использовать его настоятельно не рекомендуется.

Вместо метода stop() следует использовать метод interrupt(). В отличие от метода stop(), который принудительно останавливал поток, метод interrupt() предлагает потоку остановить свое выполнение путем установки флага interrupted в true внутри потока. Этот флаг отображает статус прерывания и имеет начальное значение false. Когда поток прерывается другим потоком, происходит одно из двух:

Есть три метода для работы с прерыванием потока:

Листинг 5:

Существуют два вида операций: блокирующие и неблокирующие. Неблокирующие операции не приостанавливают выполнения потока. К блокирующим операциям можно отнести вызовы методов sleep(), wait(), join() и, например, некоторые методы класса Socket. Если поток был прерван, пока он выполнял неблокирующие вычисления, они не будут прерваны незамедлительно. Однако поток уже отмечен как прерванный, поэтому любая следующая блокирующая операция немедленно прервется и выбросит InterruptedException.

Для обработки прерывания в потоке, который не использует блокирующие операции, следует добавить проверку флага interrupted пример в листинге 6.

Листинг 6:

public void run() <
while (Thread.currentThread().isInterrupted()) <
someHeavyComputations();
>
>

Когда в сигнатуре метода есть InterruptedException, это еще раз напоминает программисту, что этот метод блокирующий. InterruptedException сигнализирует о том, что работу потока хотят завершить. При этом не просят сделать это немедленно.

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

Во втором случае, когда InterruptedException объявить невозможно, при генерации и перехвате InterruptedException флаг interrupted устанавливается в false, и вызывающие методы не увидят, что было совершено прерывание потока. Однако можно восстановить флаг прерывания, вызвав Thread.currentThread().interrupt() при обработке прерывания.

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

Стоит внимательно следить за обработкой этого исключения когда код выполняется в threadpool. InterruptedException может быть «интересен» не только коду, но и потоку, который выполняет этот код.

Листинг 7:

try <
Object o = queue.take();
> catch InterruptedException e) <
>

Этот код некорректен, потому что поглощает (swallows) прерывание. Если этот код выполняется в tread pool, то воркер (thread pool worker) tread pool`а должен завершить исполнение, но этого не произойдёт, потому что исключение будет поглощено, и флаг будет сброшен.
Корректный код будет выглядеть так:

Листинг 8:

try <
Object o = queue.take();
> catch InterruptedException e) <
Thread.currentThread().interrupt();
>

В блоке catch происходит перехват исключения и установка флага в true.

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

2.4 Синхронизация между потоками

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

Классический пример такого поведения: два потока инкрементируют одно значение. Так как операция инкремента не выполняется за одну инструкцию процессора, то два потока изменят значение переменной произвольным образом — это называется race condition. Блоки кода, в которых может возникнуть race condition, называются критическими секциями. Чтобы избежать такой ситуации в Java предусмотрены способы синхронизации потоков.

Простейший способ синхронизации — концепция «монитора» и ключевое слово synchronized. Изначально эта концепция была введена в языке Pascal. В Java такого класса «монитор», нет, однако у каждого объекта типа Object есть свой собственный «монитор». Так как у всех классов общий родитель — Object, все они имеют свой собственный «монитор».

Концепция «монитор» внутри себя содержит 4 поля:

Рис 1. Внутреннее устройство концепции «монитора»

Blocked set, как и wait set, представляет собой неупорядоченное множество, не допускающее дубликатов. Т. е. в wait set или blocked set один и тот же поток не может быть записан два раза.

Поля монитора невозможно получить через рефлексию. У каждого объекта есть методы wait(), notify() и notifyAll(), которые этот объект унаследовал от класса Object. Использование ключевого слова synchronized гарантирует, что блоки кода будут выполняться только одним потоком в каждую конкретную единицу времени.

Есть два варианта использования ключевого слова synchronized:

Рассмотрим первую ситуацию: поток попадает в synchronized блок, выполняет критическую секцию и выходит из блока синхронизации. Ключевое слово synchronized всегда используется с объектом монитор. Сперва проверяются переменные locked и owner. Если эти поля false и null, соответственно, они заполняются. Поле locked принимает значение true, а в поле owner записывается ссылка на захватывающий поток. Как только это произошло, считается, что поток выполнил код, который соответствует открывающей фигурной скобке synchronized блока, и поток занял эту блокировку. После того как поток выполнил код, который соответствует закрывающейся фигурной скобке блока синхронизации, переменные locked и owner в мониторе очищаются.

Рассмотрим ситуацию, когда поток пытается захватить уже занятый монитор. Сначала проверяется, что переменная locked == true, затем сравнивается переменная owner. Если переменная owner не равна тому потоку, который хочет захватить монитор, то второй поток блокируется и попадает в blocked set монитора. Если сравнение переменных owner дает результат true, это значит, что один и тот же поток пытается захватить монитор повторно — в этом случае поток не блокируется. Такое поведение называется реентернабельностью. Пример такой ситуации — рекурсивные методы. После того, как блокировка освободилась, другой поток покидает blocked set и захватывает монитор. В blocked set может находится множество потоков. В этом случае выбирается произвольный поток, который далее может захватить монитор.

Листинг 9:

public class SomeClass <
private final Object PRIVATE_LOCK_OBJECT = new Object();
public synchronized void firstMethod() <
//some code
>
public void theSameAsFirstMethod() <
synchronized(this) <
//some code
>
>
public void theBestMethodUsingSynchr() <
synchronised(PRIVATE_LOCK_OBJECT) <
//some code
>
>
public static void synchronizedOnStaticMethod() <
synchronized(SomeClass.class) <
//some code
>
>
public static synchronized void synchronizedOnStaticMethod() <
//some code
>
>

Когда метод объявляется с ключевым словом synchronized, это эквивалентно коду, когда всё его тело обернуто в synchronized блок и блокировкой служит объект this. Когда статический метод используется с ключевым словом synchronized, это эквивалентно тому, когда в качестве блокировки используется объект SomeClass.class. Однако самый лучший способ — объявить private final константу, по которой и производится синхронизация. Стоит заметить, что конструкция с использованием ключевого слова synchronized — синтаксическая и проверяется компилятором. Т. е. всегда должна быть открывающая фигурная скобка и соответствующая ей закрывающая фигурная скобка synchronized блока. Synchronized блоки могут быть вложенными друг в друга (см. Листинг 10).

Листинг 10:

final Object LOCK = new Object();
synchronized(LOCK) <
synchronized(LOCK) <
synchronized(LOCK) <
>
>
>

Как показано в Листинге 10, можно несколько раз захватить монитор на одном и том же объекте. Нет способа определить, сколько раз был захвачен монитор, и не стоит строить такую логику в программе. Освобождение монитора происходит после выхода из верхнего synchronized блока. В Листинге 11 показан еще один вариант вложенных синхронизаций.

Листинг 11:

Object LOCK_A = new Object();
Object LOCK_B = new Object();
Object LOCK_C = new Object();
synchronized(LOCK_A) <
synchronized(LOCK B) <
synchronized(LOCK_C) <
>
>
>

В Листинге 11 сначала захватываются мониторы LOCK_A, затем LOCK_B и LOCK_С, а освобождаются мониторы в обратном порядке.

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

Листинг 12:

public class MainClass <
public static void main(String [] args) throws InterruptedException <
final Object lock = new Object();
lock.wait(); //будет сгенерирован IllegalMonitorStateException
>
>

Листинг 13:

public class MainClass <
private static final Object LOCK = new Object();
public static void main(String [] args) throws InterruptedException <
synchronized(LOCK) <
LOCK.wait();
>
>
>

В Листинге 13 поток main захватывает монитор объекта LOCK и вызывает метод wait() на LOCK. После вызова этого метода поток main попадает в wait set монитора LOCK. При этом монитор LOCK ОСВОБОЖДАЕТСЯ, т. е. очищается поле owner, а поле locked принимает значение false. Такое поведение гарантирует, что если какой-то другой поток захочет ожидать какого-то события на этом объекте, то он может захватить монитор LOCK и попасть в wait set.

Для того чтобы потоки, которые находятся в wait set, продолжили свое выполнение, другой поток должен захватить монитор LOCK и на LOCK вызвать методы notify() или notifyAll(). После вызова метода notify() из wait set выбирается произвольный поток и переводится в blocked set. Если был вызван метод notifyAll(), то все потоки из wait set переводятся в blocked set. Это происходит потому, что монитор LOCK занят тем потоком, который вызвал метод notify или notifyAll(). После того как этот поток выйдет из synchronized блока, нотифицированные потоки будут по одному захватывать монитор и продолжать выполнение. Методы wait(), notify() и notifyAll() используются для ожидания выполнения какого-то условия, а не для передачи данных.

Из состояния wait можно выйти несколькими способами:

Иногда поток, вызвавший метод wait на каком-то объекте блокировки, может случайно проснуться. Эта ситуация называется spurious wakeup. Случайные пробуждения случаются крайне редко (такого почти не бывает) но чтобы гарантированно избежать этого эффекта, необходимо вызывать метод wait() в цикле.

Листинг 14:

Есть два случая, когда поток может попасть в blocked set:

Рассмотрим, почему объект блокировки необходимо всегда делать закрытой неизменяемой переменной в классе private final Object obj = new Object(). Считается плохим стилем, если объект синхронизации виден снаружи класса.

Листинг 15:

class X <
public synchronized void method1() <
>
>

public class TestX <
public void someMethod(X x) <
synchronized(x) <
while(true);
>
>
>

В таком коде никакой поток не сможет вызвать метод method1() у объекта x. Все потоки, которые попытаются вызвать метод method1() у объекта x, будет заблокированы. Еще один некорректный пример в Листинге 16.

Листинг 16:

public class TestX <
public void someMethod(X x) <
synchronized(x) <
while(true) <
x.wait();
>
>
>
>

Если у объекта x будут вызывать x.notify(), цикл в Листинге 16 будет поглощать все вызовы метода notify(), т. е. поток, который выполняет код, будет всегда в wait set. Чтоб избежать таких ошибок, следует использовать private final объект-блокировку, как в одном из примеров выше. Также не следует использовать объект-блокировку для хранения какой либо информации. Это нарушает принцип single responsibility и усложняет чтение и понимание программы.

2.5 Состояния потока

У потоков есть следующие состояния:

Рис 2. Схема переходов потока из одного состояния в другое

Состояния потоков представлены в перечислении Thread.State.

2.6 Ключевое слово volatile

Ключевое слово volatile указывает, что взаимодействие с переменной в памяти должно происходить минуя кэши процессора, т. е. напрямую.

В многопоточном приложении, когда потоки используют не volatile переменные, они могут скопировать значение переменных в кэш процессора для улучшения производительности. Если в процессоре несколько ядер и каждый поток выполняется на отдельном ядре процессора, одна и та же переменная может иметь разное значение на каждом ядре процессора. В результате будет несколько копии одной и той же переменной: копии в кэше каждого ядра процессора и копия переменной в основной памяти. При использовании не volatile переменных нельзя знать наверняка, когда JVM читает значение переменной из главной памяти и когда записывается значение переменной в главную память. Это может привести к проблемам. Предположим, есть два потока, имеющих доступ к общему объекту, у которого есть счетчик (См. Листинг 17).

Листинг 17:

public class SharedObject <
public int counter = 0;
>

Предположим, что только первый поток инкрементирует переменную и оба потока могут читать переменную. Если переменная counter не volatile, то нет никакой гарантии, когда переменная будет записана в основную память, чтоб вновь измененное значение переменной увидел второй поток. Эта проблема решается путем объявления переменной counter как volatile (см. Листинг 18).

Листинг 18:

public class SharedObject <
public volatile int counter = 0;
>

бъявление переменной как volatile гарантирует, что любое чтение и любая запись в эту переменную сразу будет попадать в главную память. Объявления переменной counter как volatile достаточно, когда один поток изменяет переменную, а другой поток читает ее значение. Если два потока изменяют общую переменную, то использования ключевого слова volatile недостаточно — будет race condition. Ключевое слово volatile гарантирует следующее:

Листинг 19:

public class MyClass <
private int years;
private int months
private volatile int days;
public void update(int years, int months, int days) <
this.years = years;
this.months = months;
this.days = days;
>
>

При записи значения в volatile переменную days гарантируется, что запись остальных переменных years и months тоже будет произведена в главную память. Чтение можно выполнить следующим способом (см. Листинг 20).

Листинг 20:

public class MyClass <
private int years;
private int months
private volatile int days;
public int totalDays() <
int total = this.days;
total += months * 30;
total += years * 365;
return total;
>
public void update(int years, int months, int days) <
this.years = years;
this.months = months;
this.days = days;
>
>

В Листинге 20 в методе totalDays сначала производится чтение volatile переменной days, а затем производится чтение остальных переменных. Это чтение производится с главной памяти программы.

JVM оставляет за собой право переупорядочить инструкции для увеличения производительности, не меняя при этом семантики программы. Пример в Листинге 21.

Листинг 21:

int a = 1;
int b = 2;
a++;
b++;
//changes to
int a = 1;
a++;
int b = 2;
b++;

Рассмотрим модифицированный код из Листинга 22.

Листинг 22:

public void update(int years, int months, int days) <
this.days = days;
this.months = months;
this.years = years;
>

В Листинге 22 изменен порядок записи в volatile переменную и в обычные переменные по сравнению с примером из листинга 20. В Java есть решение проблемы перестановки инструкций, которое будет рассмотрено в следующем пункте.

В программах, которые используют многопоточность, встречаются ситуации, когда использование ключевого слова volatile недостаточно для корректной работы программы в целом. Например, есть два потока, которые одновременно изменяют общий счетчик. Необходимо прочитать значение из переменной, увеличить значение переменной, а затем записать значение в общую память. Предположим, что два потока прочитали одно и то же значение, допустим, равное единице. Каждый поток увеличил значение на 1, и первый поток записал значение 2 в главную память, а затем и второй поток записал значение 2 в общую память. Однако после записи второго потока в общую память значение должно быть 3. Такая логика приведет к race condition и некорректному поведению программы. В этом случае надо использовать ключевое слово synchronized, либо атомарные переменные, которые будут рассмотрены в последующей статье.

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

2.7 Отношение happens-before

Отношение happens-before гарантирует, что результаты операции в одном потоке будут видны в другом действии в другом потоке. Отношение happens-before определяет частичное упорядочение всех действий внутри программы. Чтобы гарантировать, что поток, выполняющий действие Y, может видеть результаты действия X (независимо от того, происходят ли X и Y в разных потоках), между X и Y должно существовать отношение happens-before. При отсутствии отношения happens-before между двумя действиями JVM может переставить операции как угодно, это происходит за счёт оптимизации компилятора JVM.

что такое поток демон java. schemes 03. что такое поток демон java фото. что такое поток демон java-schemes 03. картинка что такое поток демон java. картинка schemes 03. Потоки-демоны работают в фоновом режиме вместе с программой, но не являются неотъемлемой частью программы. Если какой-либо процесс может выполняться на фоне работы основных потоков выполнения и его деятельность заключается в обслуживании основных потоков приложения, то такой процесс может быть запущен как поток-демон. С помощью метода setDaemon(boolean value), вызванного вновь созданным потоком до его запуска, можно определить поток-демон. Метод boolean isDaemon() позволяет определить, является ли указанный поток демоном или нет.

Рис 3. Отношение happens-before

Отношение happens-before это не только перераспределение действий во времени, но и гарантия отсутствия перестановок чтения, а также записи в память. Если отношения happens-before не будет, два потока, которые читают и пишут в одно и тоже пространство памяти, могут быть последовательны в терминах времени, но не смогут последовательно увидеть изменения друг друга.

Отношение happens-before возможно в следующих случаях.

Рис 4. Отношение happens-before в одном потоке.

Рис 5. Отношение happens-before при захвате и отображения монитора.

Рис 6. Отношение happens-before при запуске потока.

Рис 7. Отношение happens-before при использовании метода join.

2.8 Заключение

В этой статье мы рассмотрели основы многопоточных программ: создание и запуск потока, свойства потока, синхронизация потоков, состояния, в которых может находится поток. Приведенные примеры демонстрируют, как корректно использовать ключевое слово synchronized и к каким последствиям может привести их неправильное использование. В конце рассказали о volatile переменных и отношении happens-before. Такие знания должны стать хорошей основой для дальнейшего изучения, понимания и написания многопоточных программ.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *