Ответ 1
Как только вы начнете многопоточность, вам было бы разумно отказаться от всех предположений о порядке, в котором будут выполняться потоки.
Если это важно, вы можете использовать операции синхронизации, но без них все ставки отключены.
Например:
public class Example1 {
public static void main(String[] args) {
Loop loop = new Loop();
loop.start(); //printing "Before start"
System.out.println("After start");
}
}
Может ли быть проблема с тем, что метод выполнения loop
заканчивается до выполнения последней строки, которая печатает "После запуска"?
Как только вы начнете многопоточность, вам было бы разумно отказаться от всех предположений о порядке, в котором будут выполняться потоки.
Если это важно, вы можете использовать операции синхронизации, но без них все ставки отключены.
Да, у вас нет контроля над тем, как потоки выполняются/планируются JVM.
Если вы хотите ввести какой-то порядок (выполнить последовательно два потока), самым простым способом сделать это было бы использование одного из объектов Lock
, которые предоставляет Java.
В основном вы спрашиваете о безопасности потоков в Java (по крайней мере, это то, что я понимаю из вопроса). Как уже упоминалось ранее, рекомендуется отказаться от любых допущений, которые могут возникнуть в отношении порядка выполнения при запуске нескольких потоков.
Однако (!), можно использовать принципы проектирования и критическое мышление для моделирования вашего приложения таким образом, чтобы в результате многопоточности не возникало нежелательных побочных эффектов.
Для начала вы можете прочитать эту статью на Проектирование инициализации объектов
Суть статьи в том, что вы должны думать о своих классах как finite state machines
. Если вы не знаете, что это такое: finite state machines
работают в наборе состояний (отсюда и название).
Идея заключается в том, что когда вы находитесь в состоянии (A)
, вы можете определить поведение, которое может выполнять это конкретное состояние: (A) -> (B), (A) -> (C)
Я могу перейти из состояния (A)
в состояние (B)
и (C)
, но не указывать (D)
(если он существует). Это мышление важно для понимания того, что ваше приложение МОЖЕТ в любой ситуации. (Изобразительные государственные машины Wikipedia)
Как только вы это понимаете, вы можете перейти на Исправление:
Существует три способа безопасного создания объектов:
Каждый подход имеет свои собственные преимущества/недостатки, которые описаны в документации Java:
Надеюсь, это даст вам лучшее представление об осложнениях, с которыми вы можете столкнуться при создании многопоточных приложений.
Если я добавлю соединение в поток и еще несколько операторов печати, можно объяснить некоторые из характеристик.
public class Example1 {
public static void main(String[] args) {
Loop loop = new Loop();
System.out.println("Before start");
loop.start(); //printing "Before start"
System.out.println("After start");
loop.join();
System.out.println("After join");
}
}
"Перед началом" всегда будет напечатано до того, как будет выполнен loop.run()
.
"После объединения" всегда будет напечатано после завершения loop.run()
.
"После запуска" не гарантируется, когда он будет напечатан относительно loop.run()
; он может быть до, он может быть после, он может быть во время выполнения чередуется с операторами печати в loop.run().
Если вам нужны гарантии, вам понадобятся другие утилит синхронизации, такие как блокировки и семафоры.