Эта серия основана на ходе переработки чисел в золото, и для того, чтобы лучше учиться, была сделана серия записей. Эта статья в основном вводит 1. Что такое поток 2. Основные операции потоков 3. Поток демона 4. Приоритет потока 5. Основные операции синхронизации потока.
1. Что такое нить
Потоки - это единицы выполнения в процессах
В процессе есть несколько потоков.
Поток - это единица выполнения в процессе.
Причина использования потоков заключается в том, что переключение процессов является очень тяжелой работой и потребляет ресурсы. Если вы используете несколько процессов, количество параллелизма не будет очень высоким. Поток - это меньшие единицы планирования и легче, поэтому потоки используются более широко используются в одновременном дизайне.
В Java концепция потоков аналогична концепции потоков на уровне операционной системы. Фактически, JVM будет отображать потоки в Java с областью потока операционной системы.
2. Основные операции потоков
2.1 Диаграмма состояния потока
На рисунке выше показана основная работа потоков в Java.
Когда поток новый, поток фактически не работает. Он просто генерирует объект, и поток фактически запускается, когда вы называете метод начала этого экземпляра. После запуска он достигает забегающего состояния. Заполнимые означает, что ресурсы потока и так далее были подготовлены и могут быть выполнены, но это не означает, что он находится в состоянии выполнения. Из -за вращения срезов времени поток может не выполняться в настоящее время. Для нас можно считать, что поток был выполнен, но на самом деле это выполняется, зависит от планирования физического ЦП. Когда задача потока выполняется, поток достигает прекращенного состояния.
Иногда, во время выполнения потока, неизбежно будет применять некоторые замки или мониторы объектов. Когда он не может быть извлечен, нить будет заблокирован, приостановлен и достигнет заблокированного состояния. Если этот поток вызывает метод ожидания, он находится в состоянии ожидания. Поток, входящий в состояние ожидания, будет ждать, пока другие потоки уведомит его. После того, как уведомление будет сделано, состояние ожидания переключится с состояния ожидания в состояние бегства, чтобы продолжить выполнение. Конечно, есть два типа ожидания, один из них должен ждать бесконечно, пока они не будут уведомлены. Он ждал ограниченный период времени. Например, если вы ждете 10 секунд или не были уведомлены, он автоматически переключится на состояние запуска.
2.2 Создайте новую ветку
Thread Think = new Thread ();
Thread.Start ();
Это открывает нить.
Одна вещь, чтобы отметить
Thread Think = new Thread ();
thread.run ();
Вы не можете открыть новый поток, вызывая метод запуска напрямую.
Метод запуска фактически вызывает метод выполнения в новой потоке операционной системы. Другими словами, если вы вызовуте метод запуска напрямую вместо метода запуска, он не запустит новый поток, но выполнит ваши операции в текущем запуска вызова потока.
Thread Think = new Thread ("T1") {@Override public void run () {// todo автоматически сгенерированный метод System.out.println (thread.currentThread (). GetName ()); }}; thread.start (); Если запуск вызывается, выход T1thread Thread = New Thread ("T1") {@Override public void run () {// todo автоматически сгенерированный метод System.out.println (thread.currentThread (). GetName ()); }}; thread.run (); Если он запускается, Main выводится. (Прямая переворачиваемая работа-это просто обычный вызов функции, и он не достиг роли мультипотчика)
Есть два способа реализации метода запуска
Первый метод - напрямую переопределить метод запуска. Как показано в коде только сейчас, наиболее удобный способ может быть достигнут с помощью анонимного класса.
Thread Think = new Thread ("T1") {@Override public void run () {// todo автоматически сгенерированный метод System.out.println (thread.currentThread (). GetName ()); }}; Второй путь
Поток T1 = новый поток (новый CreateThread3 ());
CreateThread3 () реализует запускаемый интерфейс.
В видео Чжан Сяосиан рекомендуется второй метод, говоря, что он более ориентирован на объект.
2.3 Завершите резьбу
Thread.stop () не рекомендуется. Он выпускает все мониторы
В исходном коде было четко указано, что метод остановки устарел, и причина также объясняется в Javadoc.
Причина в том, что метод остановки слишком «жестокий». Независимо от того, где выполняется поток, он немедленно перестанет отбрасывать потоки.
Когда поток записи получает блокировку, начинает писать данные. После написания идентификатора = 1 он останавливается, и блокировка выпускается при подготовке к установке name = 1. Поток чтения получает блокировку для операции чтения. Идентификационный чтение 1, а имя все еще 0, что вызывает несоответствие данных.
Самое главное, что такая ошибка не вызовет исключение и будет трудно обнаружить.
2.4 Требовая прерывание
Есть 3 способа прервать поток
public void Thread.interrupt () // Поток прерывания
общедоступная логическая тема.
Общественная статическая логическая тема.
Что такое прерывание потока?
Если вы не понимаете механизм прерывания Java, такое объяснение, скорее всего, вызовет недоразумение, и вы считаете, что призыв метода прерывания потока определенно прертит поток.
На самом деле, прерывание Java - это механизм сотрудничества. Другими словами, вызов метода прерывания объекта потока не обязательно прерывает запущенный поток, он просто требует, чтобы поток прерывал себя в нужное время. Каждый поток имеет логическое состояние прерывания (не обязательно свойство объекта, на самом деле, это состояние действительно не является полем потока), и метод прерывания просто устанавливает состояние на True. Для неблокирующих потоков состояние прерывания изменяется, то есть Thread.isErenterted () вернет true и не остановит программу;
public void run () {// поток t1 while (true) {thread.yield (); }} t1.interrupt (); Это не будет иметь никакого влияния на прерывание потока T1, но бит состояния прерывания изменяется.
Если вы хотите прекратить этот поток очень изящно, вы должны сделать это
public void run () {while (true) {if (think.currentThread (). isErengrupted ()) {System.out.println ("Interrorted!"); перерыв; } Thread.yield (); }}Использование прерываний обеспечивает определенные гарантии для согласованности данных.
Для потоков в отменяемых состояниях блокировки, таких как потоки, ожидающие эти функции, Thread.sleep (), Object.Wait () и Think.join (), этот поток будет выбросить прерывание после получения сигнала прерывания и установит состояние прерывания обратно на false.
Для потоков в разблокирующих состояниях вы можете написать код так:
public void run () {while (true) {if (think.currentThread (). isErengrupted ()) {System.out.println ("Interrorted!"); перерыв; } try {thread.sleep (2000); } catch (прерывание Exception e) {System.out.println ("Interructed, когда сон"); // Установить статус прерывания. Бит флага прерывания будет очищен после того, как выбросит нить исключения. CurrentThread (). Enterrupt (); } Thread.yield (); }}2.5 Тушка висит
Приостановить и возобновить потоки
Supesdend () не выпустит блокировку
Если блокировка происходит до резюме (), оба метода возникновения тупика являются устаревшими методами и не рекомендуются.
Причина в том, что приостановка не выпускает блокировку, поэтому ни один поток не может получить доступ к ресурсу критической области, который заблокирован им, пока он не будет возобновлен другими потоками. Поскольку последовательность выполняемых потоков не может контролироваться, если сначала запускается метод резюме других потоков, приостановленная позже всегда будет занимать блокировку, что приведет к возникновению тупика.
Используйте следующий код для моделирования этого сценария
Тест пакета; открытый тест класса {статический объект u = new Object (); staticessuspendthread t1 = new testsuspendthread ("t1"); staticessuspendthread t2 = new testsuspendthread ("t2"); Public Static Class TestSuspendThread Extends Thread {public TestSuspendThread (String name) {setName (name); } @Override public void run () {synchronized (u) {System.out.println ("in" + getName ()); Thread.currentThread (). SUSPEND (); }} public static void main (string [] args) бросает прерванные Thread.sleep (100); t2.start (); t1.resume (); t2.resume (); t1.join (); t2.join (); }} Пусть T1 и T2 соревнуются за блокировку одновременно, поток, за который соревнуются, подвешен, а затем возобновить. Говоря по логике, поток должен быть выпущен после резюме после борьбы за него, а затем другой поток конкурирует за блокировку и резюме.
Результат вывод:
в T1
в T2
Это означает, что обе потоки конкурируют за блокировку, но красный свет на консоли все еще включен, что означает, что должны быть потоки, которые не были выполнены в T1 и T2. Давайте бросим это и посмотрим
Было обнаружено, что T2 был приостановлен. Это создает тупик.
2.6 Присоединяйтесь и yeild
Yeild - нативный статический метод. Этот метод состоит в том, чтобы освободить время ЦП, а затем конкурировать с другими потоками (обратите внимание, что потоки Yeild могут по -прежнему конкурировать за процессор, обратить внимание на разницу от сна). В Javadoc также объясняется, что Yeild является методом, который в основном не используется и обычно используется в отладке и тесте.
Метод соединения означает ожидание, пока другие потоки закончится, как и код в разделе «Приостановка», вы хотите, чтобы основной поток ждал, пока T1 и T2 закончится перед окончанием. Если он не закончится, основной поток будет заблокирован там.
Пакет -тест; открытый тест класса {public volatile Static int i = 0; Public Static Class AddThread Extends Thread {@Override public void run () {for (i = 0; i <10000000; i ++); }} public static void main (string [] args) бросает прерванные at.start (); at.join (); System.out.println (i); }} Если at.join приведенного выше кода будет удален, основной поток будет работать напрямую, и значение я будет очень маленьким. Если есть соединение, значение печатного, я должен быть 10000000.
Итак, как реализовано JOIN?
Природа присоединения
в то время как (iSalive ())
{
Подождите (0);
}
Метод join () также может пройти время, что означает ожидание ограниченного периода времени, и автоматически просыпаться после этого времени.
Есть такой вопрос: кто уведомит ветку? В классе потока нет места, чтобы позвонить в уведомление?
В Javadoc было найдено соответствующее объяснение. Когда поток будет завершен и прекращен, метод уведомления будет вызван для разбуждения всех потоков, ожидающих текущего экземпляра потока. Эта операция выполняется самой JVM.
Таким образом, Javadoc также дал нам предложение не использовать WAIT и уведомление/уведомление о экземплярах потоков. Поскольку JVM позвонит, он может отличаться от ожидаемого результата вашего вызова.
3. Демон Тейд
Молча заполнение некоторых систематических услуг в фоновом режиме, таких как потоки сбора мусора и нити JIT, можно понимать как потоки демона.
Когда все процессы, не являющиеся данными, заканчиваются в приложении Java, виртуальная машина Java выйдет естественным путем.
Я написал статью о том, как ее реализовать в Python, проверьте ее здесь.
Относительно просто стать демоном в Java.
Поток t = new deamont ();
T.SetDaemon (True);
t.start ();
Это открывает ветку демона.
Пакет Test; Public Class Test {public Static Class DaemonThread Extends Thread {@Override public void run () {for (int i = 0; i <10000000; i ++) {System.out.println ("hi"); }}} public static void main (string [] args) бросает прерывание. dt.start (); }} Когда потока DT не является потоком демона, после запуска мы видим выход консоли HI
При присоединении перед началом
dt.setdaemon (true);
Консоль выходит напрямую и не имеет выхода.
4. Приоритет потока
В классе потока есть 3 переменных, которые определяют приоритет потока.
public final Static int min_priority = 1; public final Static int norm_priority = 5; public final Static int max_priority = 10; Пакет -тест; тест открытого класса {public Static Class High Extends Thread {static int count = 0; @Override public void run () {while (true) {synchronized (test.class) {count ++; if (count> 10000000) {System.out.println ("High"); перерыв; }}}}} public Static Class Low Extends Thread {static int count = 0; @Override public void run () {while (true) {synchronized (test.class) {count ++; if (count> 10000000) {System.out.println ("low"); перерыв; }}}}} public static void main (string [] args) бросает прерывания. Low Low = new LOW (); High.SetPriority (Thread.max_Priority); low.setPriority (thread.min_priority); low.start (); High.Start (); }} Пусть высокоприоритетная нить и нить с низким приоритетом одновременно конкурируют за блокировку и посмотрите, какой из них завершается в первую очередь.
Конечно, это не обязательно должно быть завершено первым с высоким приоритетом. После запуска его несколько раз я обнаружил, что вероятность завершения высокого приоритета относительно высока, но все еще возможно завершить ее в первую очередь с низким приоритетом.
5. Основная операция синхронизации потока
синхронизированный и object.wait () obejct.notify ()
Для получения подробной информации об этом разделе, пожалуйста, обратитесь к блогу, который я написал ранее.
Главное, что нужно отметить, это
Есть три способа блокировки синхронизации:
Укажите заблокированный объект: заблокируйте заданный объект и получите блокировку данного объекта, прежде чем вводить код синхронизации.
Непосредственно действует на методе экземпляра: он эквивалентен блокировке текущего экземпляра. Перед входом в код синхронизации вы должны получить блокировку текущего экземпляра.
Непосредственно действует на статических методах: это эквивалентно блокировке текущего класса. Перед входом в синхронный код вы должны получить блокировку текущего класса.
Если он работает на методе экземпляра, не новые два разных экземпляра
Он работает на статических методах, если класс одинаково, потому что добавленный блокировка - это класс. Класс, а два разных случая могут быть новыми.
Как использовать ожидание и уведомление:
Используйте любую блокировку, позвоните в ожидание и уведомить
Я не буду вдаваться в подробности в этой статье.