Наиболее часто ругаемое место на собеседованиях должно иметь знание параллельного программирования, будь вы новичок, который только начинает или CRUD-фрик с 2-3 годами опыта, а это значит, что вам зададут такой вопрос на минимум 3 года., почему бы не потратить время на борьбу до конца. Лучший способ избавиться от страха — встретиться с ним лицом к лицу, Олли! (Эта серия представляет собой заметки и резюме моего учебного процесса, а также содержит отладочный код, в который может играть каждый)
обзор последней главы
1. Какую основную проблему решает параллельное программирование?
2. Какими способами создаются и реализуются потоки?
3. Как работает new Thread() тремя способами?
Пожалуйста, просмотрите приведенные выше вопросы самостоятельно, если у вас все еще есть какие-либо вопросы, пожалуйста,Пересмотрите предыдущую главу~
Краткое содержание главы
После завершения этой главы вы生命周期
Иметь четкое представление и понимать, как выполнять преобразование между различными состояниями, и интерпретировать класс перечисления состояния потока Java. Кроме того, в этой главе основное внимание будет уделеноThread.start()
Проведите детальную интерпретацию и анализ, и в то же время вы сможете более четко разобраться в кейсеstart()
а такжеrun()
Отношение между. (Старые правила, учащиеся, знакомые с этой темой, могут поставить лайк прямо 👍, чтобы завершить эту главу)
Во-первых, жизненный цикл потока
Я думаю, что многие студенты не сердятся, когда видят эти четыре слова, их всегда об этом спрашивают на собеседованиях.生命周期
что это такое生命周期
Если вы опишете это, ваш гнев всплывет. Это правда, что это немного раздражает, но у меня нет никаких навыков.死记硬背
Это самый простой и лучший способ. Когда вы знакомы с ним, вы, естественно, поймете его при использовании и даже получите свои собственные уникальные идеи. Не говорите слишком много, просто запомните концептуальные вещи и подавайте еду~
1.НОВЫЙ этап
НОВЫЙ этап это тыnew Thread()
Этап создания объекта потока.
фокус
На этапе NEW потока вообще не существует, мы просто создалиTherad对象
, точно так же, как наш наиболее часто используемыйnew
Ключевое слово - правда. Только когда мы действительно запустим поток, наш поток будет создан в процессе JVM.
Согласно идее из предыдущей главы, после того, как мы создадим новый объект Thread, нам нужно вызватьThread.start()
чтобы запустить поток, после чего поток начнется сNEW
фазовый переход кRUNNABLE
сцена.
2. Запускаемая фаза
ТолькопередачаThread.start()
метод включения потока изNEW
фазовый переход кRUNNABLE
сцена.
Конечно, мы также можем знать из буквального значения, что нить находится в可执行转状态
Вместо реального состояния выполнения поток в это время может только ждать, пока ЦП перевернет марку, и тогда он может действительно работать. Некоторые учащиеся могут сказать, что, если ЦП не переключает марку все время? Строго говоря, вRUNNABLE
Есть только два выхода из нити, один线程意外退出
, а один проваливается процессоромRUNNING
сцена.
Здесь все выглядит так просто и красиво, создайте новый объект Thread, затем вызовите start для его запуска, а затем введите после включения процессора.RUNNING
сцена. Например,NEW
Во время этапа наша нить по-прежнему представляет собой красивый объект за пределами дворца, призывающийstart
После метода он превратился в маленького мастера во дворце, что является средней стадией.RUNNABLE
, когда будет получено право на выполнение планирования ЦП, оно будет повышено до предпочтительного娘娘
, то есть ввестиRUNNING
сцена. Вполне возможно, что в это время наша нить娘娘
Должно быть, это намного сложнее, чем красота за пределами дворца.
3. БЕГОВОЙ этап
⚠️Внимание
У студентов, которые поняли этот контент, могут возникнуть сомнения, когда они увидят это. В состоянии java-потока такого состояния нет. Почему мы выделяем это состояние для объяснения, когда говорим о жизненном цикле? Для беглости содержания главы объяснение этого содержания будет объяснено в следующем разделе, а мы продолжим говорить о нашем здесь.线程生命周期
.
хорошо давайте продолжим
Поток на этом этапе получил право на выполнение планирования ЦП, то есть он находится в рабочем состоянии.
На этом этапе поток может переходить вперед или назад:
1. Поскольку опрос планировщика ЦП приводит к тому, что поток прекращает выполнение, он войдет вRUNNABLE
сцена.
2. Активный вызов потокаyield
, отказаться от выполнения процессора вправо, он войдетRUNNABLE
сцена(这种方式并不是百分百生效的,在CPU资源不紧张的时候不会生效
).
3. позвонитьsleep
,wait
метод, введитеBLOCKED
сцена(这里讲的BLOCKED阶段和线程的BLOCKED状态需要区分开,这边讲的是一个比较广义的BLOCKED的阶段
)
4. Введите блокирующую операцию ввода-выводаBLOCKED
сцена
5. Чтобы получить ресурс блокировки, присоедините замок к очереди блокировки и введитеBLOCKED
сцена
6. Выполнение потока завершается или вызываетсяstop
методом или судя по логическому идентификатору, введите напрямуюTERMINATED
сцена
4. ЗАБЛОКИРОВАННАЯ стадия
Причина выхода на этот этап уже вRUNNING
Этап был объяснен, и здесь он не будет объясняться.Здесь мы в основном познакомим вас с тем, как можно переключать потоки на этом этапе.
1. Перейти напрямуюTERMINATED
, например, вызовstop
метод или неожиданная смерть (сбой JVM)
2. Операция блокировки потока завершается, и данные, которые вы хотите обработать, считываются или записываются вRUNNABLE
условие
3. Поток завершает сон за указанное время и входитRUNNABLE
условие
4.Wait
Состояние потокаnotify
илиnotifyall
просыпайся, входиRUNNABLE
условие
5. Получите ресурс блокировки и введитеRUNNABLE
условие
6. Процесс блокировки потока прерывается, например вызовinterrupt
метод, введитеRUNNABLE
условие
5. Статус ЗАВЕРШЕН
TERMINATED
Состояние — это конечное состояние потока, в котором поток не переключается ни в какое другое состояние, поток входит вTERMINATED
Состояние означает, что весь жизненный цикл потока завершен, введитеTERMINATED
Статус бывает трех видов:
1. Нить нормально заканчивается
2. Тема неожиданно заканчивается
3.JVM Crash
2. Как соотносятся жизненный цикл потока и состояние потока Java (анализ исходного кода)
Столкнувшись с этой проблемой, самым прямым способом демонстрации будет просмотр кода. Сначала найдите код.java.lang.Thread.State
Этот перечислимый класс.
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
можно увидеть здесьstate
Класс перечисления содержит состояния потока 6. Согласно описанию, мы будем интерпретировать эти шесть состояний одно за другим.
(1) НОВОЕ состояние = НОВЫЙ этап
* <li>{@link #NEW}<br>
* A thread that has not yet started is in this state.
* </li>
В исходном коде четко указаноNEW
состояние一个线程刚刚被创建,但是还没有启动地时候所处的状态
, это то же самое, что и наш предыдущий подразделNEW
Здесь нечего сказать, если этапы могут соответствовать.
(2) Состояние РАБОТАЕТ = стадия РАБОТАЕТ + стадия РАБОТАЕТ
* <li>{@link #RUNNABLE}<br>
* A thread executing in the Java virtual machine is in this state.
* </li>
Описание этого абзаца означает, что состояние потока, выполняющегося в виртуальной машине java, называетсяRUNNABLE
.
То есть мы объяснили отдельно в предыдущем разделеRUNNABLE阶段
а такжеRUNNING阶段
В состоянии потока вызывается однородноRUNNABLE状态
, причина, по которой мы разделяем два состояния при разделении жизненного цикла, заключается в том, что эти два состояния все же очень разные.RUNNING
отношение потока состоянияRUNNABLE
Потоки состояния немного сложнее.
(3) Состояние BLOCKED + состояние WAITING + состояние TIMED_WAITING = стадия BLOCKED
* <li>{@link #BLOCKED}<br>
* A thread that is blocked waiting for a monitor lock
* is in this state.
* </li>
* <li>{@link #WAITING}<br>
* A thread that is waiting indefinitely for another thread to
* perform a particular action is in this state.
* </li>
* <li>{@link #TIMED_WAITING}<br>
* A thread that is waiting for another thread to perform an action
* for up to a specified waiting time is in this state.
* </li>
Исходный код для этих трех состояний описывается следующим образом.
BLOCKED: состояние потока, ожидающего получения блокировки монитора и входящего в очередь блокировки.
WAITING: в этом состоянии находится поток, бесконечно ожидающий, пока другой поток выполнит определенное действие.
TIMED_WAITING: поток, ожидающий выполнения операции другим потоком, находится в этом состоянии в течение указанного времени ожидания.
студенты дляBLOCKED
а такжеWAITING
Ясна ли разница между этими двумя состояниями? Четко введите (4) напрямую~
Мы по-прежнему передаем исходный код дляstate
Описание значения перечисления для ввода темы.
государство заBLOCKED
Штаты описываются следующим образом:
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED
исходный код дляBLOCKED
Описание этого состояния следующее: поток ожидает полученияmonitor lock
когда нить находится вBLOCKED
. Проще говоря, этоsynchronized
Синхронизированный блок или метод, описываемый ключевым словом. В это время, когда другие потоки хотят получить описанный синхронизированный блок или метод, этот поток войдет и будет ждать получения.monitor lock
когда вы входитеBLOCKED
условие. Ключ здесьmonitor lock
Мониторные замки.
Метод пробуждения — блокировка целевого монитора.monitor lock
Активный релиз, в это время мы соревнуемся за блокировку мониторинга и добиваемся успеха.
состояние дляWAITING
Состояния описываются следующим образом:
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING
Здесь мы описываем, при каких обстоятельствах наш поток войдетWAITING
статус, соответственно звонитObject.wait
,Thread.join
а такжеLockSupport.park
Войдут три интерфейса APIWAITING
Статус, API потоков здесь подробно описываться не будет, он будет представлен в отдельной главе позже, здесь мы не будем о нем беспокоиться.
также объяснилWAITING
Статус — это ключ к моменту, когда поток ожидает завершения определенной операции другим потоком.两个线程,目标线程等待另一个线程完成某个动作
.
Метод пробуждения состоит в том, чтобы дождаться, пока другие потоки завершат свою собственную логику, а затем вызватьnotify
илиnotiffyall
проснуться вWAITING
состояние нити.
(4) Состояние TERMINATED = состояние TERMINATED
К этому моменту учащиеся должны были понимать жизненный цикл потока и переход между различными стадиями и в то же время понимать состояние потока Java, соответствующее жизненному циклу различных стадий. Далее следует анализ исходного кода второго абзаца этой главы~
Сосредоточьтесь на этом, посмотрите, как усталые одноклассники делают перерыв, а затем анализ исходного кода start()
3. Интерпретация исходного кода шаблона проектирования Thread.start()
Прежде чем мы начнем, давайте подумаем, почемуstart()
можно начать нашу тему,run()
не можешь?
Не говори глупостей, сначала зайди в исходный код
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
Старая рутина, давайте сначала посмотрим на метод
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
переводить:start()
Метод запускает поток, и виртуальная машина Java вызывает егоrun()
метод для выполнения логической единицы потока. И потоку разрешено запускаться только один раз, это незаконно запускать поток несколько раз, он выдастIllegalThreadStateException
аномальный.
Ниже мы объясним через три шагаThread.start()
(1) Определить значение threadStatus
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
threadStatus==0
выражатьNEW
состояние, если звонитThread.start()
узнать, когдаthreadStatus!=0
то это означает, что потока больше нет вNEW
состояние, вызов этого метода в настоящее время незаконен, поэтому бросьтеIllegalThreadStateException
аномальный. (Такое простое логическое суждение, я считаю, что у моих одноклассников должно быть такое же представление, как и у меня, "я тоже так умею 😁")
(2) Присоединяйтесь к группе тем
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
переводить: уведомить группу о том, что новый поток был запущен и его необходимо добавить в группу потоков, одновременно уменьшая количество неначатых.
Прочитав перевод, не знаю, что сказать, продолжаем следитьadd
void add(Thread t) {
synchronized (this) {
if (destroyed) {
throw new IllegalThreadStateException();
}
if (threads == null) {
threads = new Thread[4];
} else if (nthreads == threads.length) {
threads = Arrays.copyOf(threads, nthreads * 2);
}
threads[nthreads] = t;
// This is done last so it doesn't matter in case the
// thread is killed
nthreads++;
// The thread is now a fully fledged member of the group, even
// though it may, or may not, have been started yet. It will prevent
// the group from being destroyed so the unstarted Threads count is
// decremented.
nUnstartedThreads--;
}
}
add
Метод делится на три шага
первый шаг:
Определить, была ли уничтожена группа потоков, если группа потоков больше не существует, затем броситьIllegalThreadStateException
аномальный.
if (destroyed) {
throw new IllegalThreadStateException();
}
Шаг 2: Добавьте поток в массив групп потоков.
if (threads == null) {
threads = new Thread[4];
} else if (nthreads == threads.length) {
threads = Arrays.copyOf(threads, nthreads * 2);
}
threads[nthreads] = t;
// This is done last so it doesn't matter in case the
// thread is killed
nthreads++;
Судите первымthreads
Если он пуст, массив создается, если он пуст, и начальная длина равна 4. еслиthreads
Не пустой, суждениеnthreads == threads.length
Если true, развернуть массив потоковthreads = Arrays.copyOf(threads, nthreads * 2)
, добавьте входной параметр вthreads[]
, индексы накапливаютсяnthreads++
Шаг 3. Уменьшите количество незапущенных потоков на один
// The thread is now a fully fledged member of the group, even
// though it may, or may not, have been started yet. It will prevent
// the group from being destroyed so the unstarted Threads count is
// decremented.
nUnstartedThreads--;
(3) Вызовите метод start0(), чтобы запустить поток
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
started
Это состояние запуска потока, значение по умолчанию — false, запуск успешен, а затем ему присваивается значение true. тогда позвониstart0()
метод
private native void start0();
В этот момент мы вспоминаем, что логика выполнения нашего потока написана наrun()
, то весь процесс находит, что звонить некудаrun()
,только этоstart0()
Способ самый подозрительный. Видно, что этот метод является本地方法
,существуетThread.java
В начале определения класса есть статические блоки для завершения регистрации.
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
ОК, остановись здесь, мы будем привлекать некоторый контент в нижней части JVM, мы не будем делать расширение здесь, нам просто нужно знатьstart()
внутренний звонок звонокstart0()
метод, который, наконец, вызывается внутри потока после создания нашего потока.run()
метод.
Нам просто нужно помнить, что место, где фактически создается поток и вызывается метод запуска, выполняется jvm, Мы здесь просто как инициатор запуска, чтобы завершить действие по отправке сигнала запуска потока. Мы больше не будем вести вас к куче CPP-файлов, чтобы пройтись по облакам и дождю, дабы избежать укачивания.
На данный момент мы должны четко понимать проблему в начале этого раздела,为什么start()能启动我们的线程,run()不行呢
.相信大家已经知道了原因了,因为run
Может использоваться только как внутренний класс реализации, если вы просто вызываетеrun
мы просто реализовали логику в этом потоке и на самом деле не открывали новый поток для выполнения нашей логики. Для открытия нового потока требуется вызовstart0()
Позвольте JVM сделать за нас создание, так что звоните толькоstart()
метод запуска потока.
Здесь я также написал небольшой пример для проверки:
public static class TestStartAndRun implements Runnable{
@Override
public void run() {
System.out.println("我的名字叫"+Thread.currentThread().getName());
}
}
public static void main(String[] args) {
new Thread(new TestStartAndRun(),"start").start();
new Thread(new TestStartAndRun(),"run").run();
}
выход:
我的名字叫start
我的名字叫main
Можно видеть, что наше определенное имя под названием «Беги» не запускается, и это наш собственный.main
Тема, это доказываетrun
Невозможно создать поток, но можно выполнять внутренние классы в текущем потокеrun
, и может выполняться несколько раз.
В-четвертых, расширенное чтение - имитируйте шаблон проектирования стартового шаблона, чтобы написать аналогичную программу самостоятельно.
/**
* 杯子
*/
final void Teacup(String nothing){
System.out.print("今天");
Brewing(nothing);
System.out.println(",愉快的一天开始啦!");
}
/**
* 泡点什么呢
* @param nothing
*/
void Brewing(String nothing){
}
public static void main(String[] args) {
TemplateDesignExample t1 = new TemplateDesignExample(){
@Override
void Brewing(String nothing){
System.out.print("下雨,"+nothing);
}
};
t1.Teacup("早上喝咖啡");
TemplateDesignExample t2 = new TemplateDesignExample(){
@Override
void Brewing(String nothing){
System.out.print("晴天,"+nothing);
}
};
t2.Teacup("早上喝茶");
}
выход:
今天下雨,早上喝咖啡,愉快的一天开始啦!
今天晴天,早上喝茶,愉快的一天开始啦!
Таким образом, будь то дождь или солнце, мы можем пить, что хотим, и мы можем счастливо программировать каждый день.Если мы можем быть внимательными и лайкать 👍, каждый день становится вдвойне счастливым!
Я желаю студентам счастливых праздников Праздника лодок-драконов, и не забывайте заниматься со мной во время каникул~