Однажды я шел по дороге и время от времени натыкался на метод Thread.join(), поэтому я пошел в Интернет, чтобы найти соответствующую информацию, но я также был в недоумении (туалетная вода). После того, как я понял это в течение длительного времени, я хочу объяснить метод join(), который я понимаю, на диаграмме.
1. Я рисую
Фото открытия:
John
Текущий процесс потока.
Не волнуйтесь, посмотрите его дважды! Мы можем видеть, что после того, как основной поток вызовет метод join(), указанный поток будет захвачен и честно поставлен в очередь позади.
2. Угадай
💡 В: Ответьте, пожалуйста, в чем разница между запущенным результатом невыполнения метода join() и выполнением метода join() (результатов может быть несколько, время выполнения, порядок вывода)?
🌱 Ответ:
тип | количество возможных исходов | скорость бега | порядок вывода | Временная сложность (лучшая) |
---|---|---|---|---|
не используйте соединение () | 6 видов | быстрый (многопоточный | вышел из строя | O(1) |
использовать соединение () | 1 тип | медленный (один поток) | приказ | O(n) |
- После прочтения у вас могут остаться вопросы:
💡 В: Почему число возможных результатов без использования join() равно 6? Почему порядок вывода не соответствует порядку?
🌱 A: Из-за многопоточности, несмотря на то, что start() последовательно выполняет поток 1/2/3, определенный поток может быть завершен с упреждением по разным причинам, что приводит к 6 видам результатов выполнения:
123
132
213
231
321
312
- Многопоточность не честна и сложна (да, не невозможна) в управлении.
💡 В: Почему он тормозит после использования join()?
🌱 A: Потому что после использования join() три потока будут переведены в основной поток для последовательного запуска.В настоящее время они не могут работать одновременно, а могут запускаться только один за другим.
Три, практический
Не поленитесь, вы еще молоды, откройте свою IDE и вставьте код ниже:
public class TestJoin {
public static void main(String[] args) {
John john1 = new John();
John john2 = new John();
John john3 = new John();
try {
john1.start();
john1.join();
john2.start();
john2.join();
john3.start();
john3.join();
} catch (InterruptedException IE) {
IE.printStackTrace();
}
}
}
class John extends Thread {
@Override
public void run() {
for (int i = 0; i < 2; i++)
{
try
{
Thread.sleep(500);
System.out.println("Current Thread: "
+ Thread.currentThread().getName());
}catch(Exception ex){
System.out.println("Exception has" +
" been caught" + ex);
}
System.out.println(i);
}
}
}
результат операции:
Current Thread: Thread-0
0
Current Thread: Thread-0
1
Current Thread: Thread-1
0
Current Thread: Thread-1
1
Current Thread: Thread-2
0
Current Thread: Thread-2
1
- Видно, что все три интенсивно работающих потока подключаются к основному потоку для последовательного выполнения.
Теперь удалите две строки кода:
john2.join();
john3.join();
запустить снова:
Current Thread: Thread-0
0
Current Thread: Thread-0
1
Current Thread: Thread-1
0
Current Thread: Thread-2
0
Current Thread: Thread-2
1
Current Thread: Thread-1
1
- Видно, что два потока john2 и john3 не выполняются в основном потоке, поэтому результаты выполнения более свободны, а время выполнения также сокращается.
Обратите внимание, что вы должны использовать метод start() для запуска потока перед использованием метода join(). Нить вообще не работает, так как же ее перетянуть?
Анализ исходного кода:
/**
* Waits for this thread to die.
* 等待该线程死亡
*/
public final void join() throws InterruptedException {
join(0);
}
/**
* Waits at most {@code millis} milliseconds for this thread to die
* 最多等待code毫秒为该线程死亡,就是说如果该线程死亡时间小于该code则提前结束,继续往下执行
* 如果该线程在code毫秒内没有死亡,到时间了则不管该线程,继续往下执行
*. A timeout of {@code 0} means to wait forever.
* 如果时间code=0则永久等待,直到该线程消亡
*/
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
В исходном коде:
- То есть присоединиться () = присоединиться (0)
- Если временной код millis=0, он будет ждать бесконечно, пока поток не умрет.
- Если время больше 0, поток, вызывающий метод соединения, будет уведомлен о продолжении выполнения независимо от того, равно ли время выполнения потока этому времени или нет, то есть notifyAll
public static void main(String[] args) {
John john1 = new John();
John john2 = new John();
John john3 = new John();
try {
john1.start();
john1.join();
john2.start();
john2.join(5);
john3.start();
john3.join();
} catch (InterruptedException IE) {
IE.printStackTrace();
}
}
Результаты
Current Thread: Thread-0
0
Current Thread: Thread-0
1
Current Thread: Thread-1
0
Current Thread: Thread-2
0
Current Thread: Thread-1
1
Current Thread: Thread-2
1
- Видно, что поток 3 запускается и начинает выполняться через 5 миллисекунд после того, как поток присоединится к основному потоку.Использование join() на самом деле является координацией потоков, упорядоченным выполнением потоков и контролем потоков в планировании. времени~
4. Конец
К этому моменту вы освоили использование метода join(). Не спрашивайте меня, для чего его можно использовать, вам не придется возиться, когда вам понадобится его функциональность.
Серьезно, вы дочитали до конца. Давайте снова поговорим о методе yield() (о нем говорилось в других статьях):
Функция метода yield() не имеет ничего общего с методом join(). Когда вы выполняете метод yield() в потоке, поток попытается приостановить себя и уступить место потоку того же уровня, который находится в очереди позади (т. е. уступить место), это нестабильный процесс (не обязательно эффективный, также не обязательно когда продолжать).
Кроме того, рекомендуется воспользоваться тем, что вы можете узнать, а затем просмотреть аналогичные знания о syncronized, wait(), notify().
Посмотрите на карту, и я нашел ее для вас: