Оглавление(?)[-]
Один расширяет JavalangThread Class Two внедряет javalangrunnable интерфейс три различия между потоком и запустившимся переходом с четырьмя состоянием потока Пяти резьбовых планирования шесть распространенных функций, объясняющих, как использовать метод соединения Семь распространенных поток. Объяснение.
В этой статье в основном рассказывается о методах использования мультиподбота в Java, синхронизации потоков, передаче данных потока, состоянии потока и соответствующем использовании и обзоре функций потока.
Во -первых, давайте поговорим о разнице между процессом и потоком:
Процесс: Каждый процесс имеет независимый код и пространство данных (контекст процесса), а переключение между процессами будет иметь большие накладные расходы. Процесс содержит 1-N потоков.
Тема: тот же тип потоков совместно использует код и пространство данных. Каждый поток имеет независимый работающий стек и счетчик программы (ПК), а переключение потока невелика.
Как и процесс, потоки разделены на пять этапов: создание, готовое, бег, блокировка и завершение.
Многопроцесс означает, что операционная система может выполнять несколько задач (программы) одновременно.
Многопользовательский состав относится к нескольким последовательным потокам, выполняемым в одной и той же программе.
В Java есть два способа реализации многопоточного. Одним из них является продолжение класса потоков, а другой - реализовать запускаемый интерфейс.
1. Расширить класс java.lang.thread
пакет com.multithread.learning;/***@Functon MultiThreading Learning*@Автор Lin Bingwen*@Time 2015.3.9*/Class Thread1 Extends Thread {Private String name; public Thread1 (String name) {this.name = name; } public void run () {for (int i = 0; i <5; i ++) {System.out.println (name + "run:" + i); try {sleep ((int) math.random () * 10); } catch (прерванное искусство e) {e.printstacktrace (); }} / Thread1 MTH2 = New Thread1 ("B"); mth1.start (); mth2.start (); }} Выход:
Пробег: 0
B Беги: 0
Пробег: 1
Пробег: 2
Пробег: 3
Пробег: 4
B Беги: 1
B Беги: 2
B Беги: 3
B Беги: 4
Запустите снова:
Пробег: 0
B Беги: 0
B Беги: 1
B Беги: 2
B Беги: 3
B Беги: 4
Пробег: 1
Пробег: 2
Пробег: 3
Пробег: 4
иллюстрировать:
Когда программа запускается и запускается Main, виртуальная машина Java запускает процесс, а основной поток Main создается при вызове Main (). С помощью метода начала двух объектов Mitisay также запускаются две потоки, так что все приложение работает в нескольких потоках.
ПРИМЕЧАНИЕ. Метод Start () называется не для немедленного выполнения многопоточного кода, а вместо этого заставляет поток стать запущенным состоянием. Когда он работает, определяется операционной системой.
Из результатов работы программы мы можем обнаружить, что многопоточные программы выполняются вне порядка. Следовательно, только код, выполненный вне порядка, должен быть спроектирован как многопоточный.
Цель метода Thread.sleep () - это предотвращение того, чтобы текущий поток не занимал ресурсы ЦП, полученные только в процессе, чтобы оставить определенное количество времени для выполнения других потоков.
Фактически, порядок выполнения всего многопоточного кода является неопределенным, и результаты каждого выполнения являются случайными.
Однако, если метод начала называется неоднократно, будет происходить java.lang.illegalthreadstateException.
Thread1 mth1 = new Thread1 ("a"); Thread1 MTH2 = MTH1; mth1.start (); mth2.start (); Выход:
Исключение в потоке "Main" java.lang.illegalthreadStateException
на java.lang.thread.start (неизвестный источник)
at com.multithread.learning.main.main (main.java:31)
Пробег: 0
Пробег: 1
Пробег: 2
Пробег: 3
Пробег: 4
2. Реализуйте интерфейс java.lang.runnable
/***@Functon MultiThreading Learning*@Автор Lin Bingwen*@Time 2015.3.9*/Package com.multithread.runnable; Class Thread2 реализует Runnable {private String name; public Thread2 (string name) {this.name = name; } @Override public void run () {for (int i = 0; i <5; i ++) {System.out.println (name + "run:" + i); try {thread.sleep ((int) math.random () * 10); } catch (прерванное искусство e) {e.printstacktrace (); }} / новый поток (новый Thread2 ("D")). start (); }} Выход:
C запустить: 0
D запустить: 0
D Беги: 1
C запустить: 1
D Беги: 2
C запустить: 2
D Беги: 3
C запустить: 3
D Беги: 4
C Run: 4
иллюстрировать:
Класс Thread2 реализует запускаемый интерфейс, который заставляет класс имеет характеристики многопоточного класса. Метод run () является соглашением для многопоточных программ. Весь многопоточный код находится в методе выполнения. Класс потоков на самом деле является классом, который реализует запускаемый интерфейс.
При запуске многопоточного чтения необходимо сначала построить объект через поток конструктора класса потока (выполняемая цель), а затем вызовать метод start () объекта потока, чтобы запустить код с мультипоточником.
Фактически, весь многопоточный код запускается с помощью метода запуска Thread () start (). Следовательно, будь то расширение класса потока или реализацию запускаемого интерфейса для реализации многопоточного или в конечном итоге управления потоками через API объекта потока, знакомство с API класса потоков является основой для многопоточного программирования.
3. Разница между потоком и запусками
Если класс наследует поток, он не подходит для обмена ресурсами. Однако, если реализован выполняемый интерфейс, легко реализовать обмен ресурсами.
пакет com.multithread.learning;/***@functon многопоточное обучение, наследственное поток, ресурсы не могут быть переданы*@Автор Lin Bingwen*@Time 2015.3.9*/Class Thread1 Extends Thread {private int count = 5; Приватное название строки; public Thread1 (String name) {this.name = name; } public void run () {for (int i = 0; i <5; i ++) {System.out.println (name + "run count =" + count-); try {sleep ((int) math.random () * 10); } catch (прерванное искусство e) {e.printstacktrace (); }} / Thread1 MTH2 = New Thread1 ("B"); mth1.start (); mth2.start (); }} Выход:
B Run Count = 5
A run count = 5
B Run Count = 4
B Run Count = 3
B Run Count = 2
B Run Count = 1
A run count = 4
A run count = 3
A run count = 2
A run count = 1
Из вышесказанного мы видим, что количество различных потоков, которые будут иметь большую проблему для системы продажи билетов. Конечно, здесь можно использовать синхронизация. Давайте использовать запускаемые, чтобы сделать это здесь
/***@Functon Multi-Threading Learning Унаследованное, ресурсы могут быть обменены*@Автор Lin Bingwen*@Time 2015.3.9*/Package com.multithread.runnable; Class Thread2 реализует Runnable {private int count = 15; @Override public void run () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). GetName () + "run count =" + count-); try {thread.sleep ((int) math.random () * 10); } catch (прерванное искусство e) {e.printstacktrace (); }}}}} открытый класс main {public static void main (string [] args) {thread2 my = new Thread2 (); Новый поток (my, "c"). start (); // Тот же MT, но в потоке это невозможно. Если вы создаете создание объекта MT, исключение появится новое поток (My, "D"). Start (); новая ветка (My, "e"). start (); }} Выход:
C Run Count = 15
D забеги = 14
E run count = 13
D забеги = 12
D забеги = 10
D запустить счет = 9
D забеги = 8
C Run Count = 11
E run count = 12
C Run Count = 7
E Run Count = 6
C Run Count = 5
E run count = 4
C Run Count = 3
E run count = 2
Здесь мы должны отметить, что каждый поток использует один и тот же объект экземпляра. Если это не то же самое, эффект будет таким же, как и выше!
Суммировать:
Преимущества реализации запускаемого интерфейса по сравнению с наследством класса потока:
1): Подходит для нескольких потоков с одним и тем же программным кодом для обработки одного и того же ресурса
2): может избежать ограничения единого наследования на Java
3): Увеличьте надежность программы, код может быть разделен по нескольким потокам, а код и данные являются независимыми
Позвольте мне напомнить вам: основным методом является поток. В Java нить запускаются одновременно. Что касается того, когда и какой из них выполняется в первую очередь, это полностью зависит от того, кто получает ресурсы процессора в первую очередь.
В Java не менее 2 потоков запускаются каждый раз, когда программа работает. Один из них является главной нитью, а другая - нить сбора мусора. Потому что всякий раз, когда класс выполняется с использованием команд Java, фактически будет запущен JVM, и каждая стажировка JVM начинает процесс в операционной системе.
4. Переход состояния потока
1. Новое состояние (новое): создан новый объект потока.
2. Готовое состояние (запускаемое): после создания объекта потока другие потоки вызывают метод start () объекта. Поток в этом состоянии расположена в пуле запускаемых потоков и становится запускаемой, ожидая, чтобы получить права на использование ЦП.
3. Запуск состояния: поток в штате Готово приобретает процессор и выполняет код программы.
4. Заблокированное состояние: заблокированное состояние означает, что поток отдает права на использование процессора по какой -то причине и временно прекращается. Только когда поток не вступит в состояние готового состояния, у нее есть шанс перейти в состояние бега. Есть три типа блокировки:
(1) Ожидание блокировки: работающий поток выполняет метод wait (), и JVM поместит поток в бассейн ожидания.
(2) Синхронное блокирование: когда работающий поток получает блокировку синхронизации объекта, если блокировка синхронизации занята другими потоками, JVM помещает потоку в пул блокировки.
(Iii), Другое блокировку: когда работающий поток выполняет метод Sleep () или join (), или выпускает запрос ввода/вывода, JVM установит поток в состояние блокировки. Когда состояние Sleep () истекло, join () ждал, пока поток заканчивается или истекло, или обработка ввода-вывода была завершена, поток снова въехал в состояние готового состояния.
5. Мертвое состояние: поток завершил выполнение или выход метода run () из -за исключения, и поток заканчивает свой жизненный цикл.
5. Планирование потоков
Планирование потоков
1. Отрегулируйте приоритет потока: Java Threads имеют приоритет, а потоки с высоким приоритетом получат больше возможностей для работы.
Приоритет потоков Java представлен целыми числами, с диапазоном значений 1 ~ 10. Класс потоков имеет следующие три статические константы:
Статический int max_priority
Самый высокий приоритет, который может иметь нить, составляет 10.
Статический int min_priority
Самый низкий приоритет, который может иметь поток, составляет 1.
Статический int norm_priority
Приоритет по умолчанию, присвоенный потоку, составляет 5.
Методы setPriority () и getPriority () класса потока используются для установки и получения приоритета потока соответственно.
Каждый поток имеет приоритет по умолчанию. Приоритетом по умолчанию основного потока является Thread.norm_priority.
Приоритет потоков наследуется. Например, если поток B создается в потоке A, то B будет иметь тот же приоритет, что и A.
JVM предоставляет 10 приоритетов потока, но он плохо отображается с общими операционными системами. Если вы хотите, чтобы программа была перенесена в каждую операционную систему, вы должны использовать класс потоков только со следующими тремя статическими константами в качестве приоритета, что может убедиться, что тот же приоритет принимает тот же метод планирования.
2. Метод нить сон: нить. Параметр Millis устанавливает время сна в миллисекундах. Когда сон закончится, он становится заполненным. Платформа Sleep () имеет хорошую портативность.
3. Поток ждать: метод wait () в классе объекта заставляет текущий поток ждать, пока другие потоки вызовут метод объекта notify () или метод пробуждения notifyall (). Эти два метода пробуждения также являются методами в классе объекта, и их поведение эквивалентно вызову ожидания (0).
4. Концессии потока: метод Thread.yield () приостанавливает объект выполнения в настоящее время и предоставляет возможность выполнения потоками с тем же или более высоким приоритетом.
5. СООТВЕТСТВЕННОЕ СОВЕРКИ: join () Метод, ожидающий завершения других потоков. Вызов метод join () другого потока в текущем потоке, текущий поток переходит в состояние блокировки до тех пор, пока другой процесс не запустится, а текущий поток не переходит от блокировки до состояния готового.
6. Пробуждение потока: метод notify () в классе объекта разбудит один поток, ожидающий этого монитора объекта. Если все потоки ждут этого объекта, будет выбран один из потоков. Выбор произволен и происходит при принятии решения по реализации. Поток ждет на мониторе объекта, вызывая один из методов ожидания. Поток Wheaken не может быть выполнен до тех пор, пока текущий поток не отказатся от блокировки на этом объекте. Внутренний поток будет конкурировать со всеми другими потоками, которые активно синхронизируются на объекте обычным образом; Например, поток Wowken не имеет надежных привилегий или недостатков в следующем потоке, который блокирует этот объект. Подобный метод также имеет notifyall (), который разбудит все потоки, ожидающие этого монитора объекта.
ПРИМЕЧАНИЕ. Два метода приостановки () и resume () в потоке были отменены в JDK1.5 и не будут введены снова. Потому что есть тенденция к тупику.
6. Описание общих функций
① Sleep (Long Millis): пусть в настоящее время выполняется поток в течение указанного количества миллисекунд (приостановка выполнения)
②join (): Относится к ожиданию, пока поток T прекратит прекращение.
Как его использовать.
соединение - это метод класса потока. Это называется непосредственно после запуска потока. То есть функция join (): «Подождите, пока поток прекратит». Что нужно понять здесь, так это то, что поток относится к основному потоку, ожидающему прекращения дочернего потока. То есть код после того, как детская потока вызывает метод join (), и его можно выполнить только до тех пор, пока детский поток не будет закончен.
Thread t = new athread (); t.start (); t.join ();
Зачем использовать метод joint ()
Во многих случаях основной поток генерирует и запускает дочернюю нить. Если в режиме ребенка требуется большое количество трудоемких операций, основной поток часто заканчивается перед детской резьбой. Однако, если основной поток должен использовать результат обработки детского потока после обработки других транзакций, то есть основной поток должен ждать, пока дочерний поток завершит выполнение перед окончанием. В настоящее время метод join () должен использоваться.
Нет присоединения. /** *@Functon MultiThreading Learning, присоединяйтесь *@Автор Lin Bingwen *@Time 2015.3.9 */package com.multithread.join; Class Thread1 Extends Thread {Private String name; public Thread1 (string name) {super (name); this.name = name; } public void run () {System.out.println (Thread.currentThread (). getName () + "Запускается поток!"); for (int i = 0; i <5; i ++) {System.out.println ("subthread"+name+"run:"+i); try {sleep ((int) math.random () * 10); } catch (прерванное искусство e) {e.printstacktrace (); }} System.out.println (thread.currentThread (). GetName () + "Запуск потока ends!"); }} открытый класс main {public static void main (string [] args) {System.out.println (thread.currentThread (). getName ()+"Main Tread Run Start!"); Thread1 mth1 = new Thread1 ("a"); Thread1 MTH2 = New Thread1 ("B"); mth1.start (); mth2.start (); System.out.println (Thread.currentThread (). GetName ()+ "Основной запуск поток запуска!"); }} Результат вывода:
Основная главная тема начинает работать!
Основная основная потока заканчивается!
B Начнется запуск нить!
Детская нить B пробегает: 0
Запуск нить начинается!
Детская нить пробеги: 0
Детская нить B пробегает: 1
Детская нить пробеги: 1
Детская нить пробеги: 2
Детская нить пробеги: 3
Детская нить пробеги: 4
Заканчивается потоком заканчивается!
Детская нить B пробеги: 2
Детская нить B пробеги: 3
Детская нить B пробеги: 4
B Поток бежит!
Обнаружил, что основная нить закончилась раньше, чем детская нить
Присоединиться
открытый класс main {public static void main (string [] args) {System.out.println (thread.currentThread (). getName ()+"Main Tread Run Start!"); Thread1 mth1 = new Thread1 ("a"); Thread1 MTH2 = New Thread1 ("B"); mth1.start (); mth2.start (); try {mth1.join (); } catch (прерванное искусство e) {e.printstacktrace (); } try {mth2.join (); } catch (прерванное искусство e) {e.printstacktrace (); } System.out.println (thread.currentThread (). GetName ()+ "Основной запуск поток заканчивается!"); }} Результаты работы:
Основная главная тема начинает работать!
Запуск нить начинается!
Детская нить пробеги: 0
B Начнется запуск нить!
Детская нить B пробегает: 0
Детская нить пробеги: 1
Детская нить B пробегает: 1
Детская нить пробеги: 2
Детская нить B пробеги: 2
Детская нить пробеги: 3
Детская нить B пробеги: 3
Детская нить пробеги: 4
Детская нить B пробеги: 4
Заканчивается потоком заканчивается!
Основной поток обязательно подождут, пока дочерние потоки не закончатся до того, как она закончится.
③yield (): приостанавливает в данный момент объект выполнения потока и выполняет другие потоки.
Функция метода Thread.yield (): приостановите в данный момент объект выполнения в данный момент и выполните другие потоки.
Что должен сделать (), чтобы вернуть текущий запуск потока в бегство, чтобы позволить другим потокам с тем же приоритетом, чтобы получить возможность запустить. Следовательно, цель использования доходности () состоит в том, чтобы позволить потокам того же приоритета выполнять надлежащим образом. Однако в действительности, helive () не может быть гарантированно достичь цели концессии, поскольку концессионная поток может быть снова выбрана Планировщиком потока.
Заключение: helld () никогда не заставляет нить переходить в состояние ожидания/сна/блокировки. В большинстве случаев доход () приведет к тому, что поток переходит от запуска состояния, но может не работать. Вы можете увидеть картинку выше.
/** *@Functon MultiThreading Learning Learning *@Автор Lin Bingwen *@Time 2015.3.9 */package com.multithread.yield; class threadyield extends thread {public Threadyield (String name) {super (name); } @Override public void run () {for (int i = 1; i <= 50; i ++) {System.out.println ("" + this.getName () + "-----" + i); // Когда мне будет 30 лет, поток отказатся от времени процессора и позволит выполнить другие или его собственные потоки (то есть тот, кто его захватит сначала выполняет его), если (i == 30) {this.yield (); }} / Threadyield yt2 = new Threadyield ("li si"); yt1.start (); yt2.start (); }} Результаты работы:
Первый случай: Li Si (поток) получит время процессора, когда он будет выполнен до 30.
Вторая ситуация: когда Li Si (поток) выполняется до 30, время процессора будет оставлено. В настоящее время Li Si (поток) захватывает время процессора и выполняет его.
Разница между сна () и урожайностью ()
Разница между Sleep () и урожайностью ()): Sleep () заставляет текущий поток войти в застойное состояние, поэтому поток, выполняющий Sleep (), безусловно, не будет выполняться в течение указанного времени; hield () просто приводит к возвращению текущего потока в исполняемое состояние, поэтому поток, выполняющий доход (), может быть выполнен сразу после вступления в исполняемое состояние.
Метод сна заставляет в настоящее время проводной потоки спать в течение определенного периода времени и входит в незавершенное состояние. Длина этого периода установлена программой. Метод доходности позволяет текущему потоку отказаться от владения процессором, но время передачи неконтролируется. Фактически, метод доходности () соответствует следующей операции: сначала проверьте, существуют ли потоки с тем же приоритетом в настоящее время в том же запущенном состоянии. Если это так, передайте право собственности на ЦП в эту ветку, иначе продолжайте запускать исходный поток. Таким образом, метод доходности () называется «концессией», что дает возможность пробежать возможности для других потоков с тем же приоритетом
Кроме того, метод сна позволяет потокам с более низким приоритетом получать возможности для запуска, но когда метод доходности () выполняется, текущий поток все еще находится в запутанном состоянии, поэтому невозможно отказаться от более низких приоритетных потоков, чтобы получить право собственности на ЦП позже. В запущенной системе, если поток с более высоким приоритетом не вызывает метод сна и не блокируется с помощью ввода/вывода, то нить с более низким приоритетом может ждать только все более высокие приоритетные потоки, чтобы иметь возможность запустить.
④SetPriority (): изменить приоритет потока.
Min_priority = 1
Norm_priority = 5
Max_priority = 10
Использование:
Thread4 T1 = new Thread4 ("T1");
Thread4 T2 = new Thread4 ("T2");
T1.SetPriority (Thread.max_Priority);
t2.setPriority (Thread.min_priority);
⑤Intertrupt (): прервать поток. Этот метод окончания довольно грубый. Если T -поток открывает ресурс и не успел его закрыть, то есть метод прогона вынужден закончить поток до его выполнения, что приведет к закрытию ресурса.
Лучший способ положить конец процессу - использовать пример программы функции Sleep (). Логическая переменная используется в классе потока для управления, когда метод run () заканчивается. Как только метод run () заканчивается, поток заканчивается.
⑥ wait ()
Obj.wait () и obj.notify () должны использоваться с синхронизированным (obj), то есть, ожидание и уведомление о работе на приобретенной блокировке OBJ. С синхронизированной точки зрения, это obj.wait (), и obj.notify должен быть в блоке синхронизированного (obj) {...}. С функциональной точки зрения, ожидание означает, что после получения блокировки объекта он активно выпускает блокировку объекта, а поток спит. Замок объекта не может быть получен, и выполнение будет продолжаться до тех пор, пока другой поток не вызовет объект notify (), чтобы разбудить поток. Соответствующее уведомление ()-это операция пробуждения блокировки объекта. Но одно можно отметить, что после вызова () вызов notify () блокировка объекта не выпускается немедленно, но выполнение соответствующего блока Synchronized () {} завершается блокировкой, и блокировка автоматически выпускается, JVM случайным образом выберет поток из потока блокировки wait (), присваивайте его блокировке объекта, разбудит поток и продолжить выполнение. Это обеспечивает синхронизацию и операции пробуждения между потоками. Оба Thread.sleep () и Object.Wait () могут приостановить текущий поток и высвободить элемент управления ЦП. Основное отличие состоит в том, что в то время как Object.Wait () выпускает процессор, он высвобождает управление блокировкой объекта.
Этого недостаточно, чтобы понять концептуально, и его нужно проверить в практических примерах, чтобы лучше понять. Наиболее классическим примером применения object.wait () и object.notify () должен быть проблема печати ABC с тремя потоками. Это относительно классический вопрос интервью, и вопросы следующие:
Установите три потока, резьба A напечатает 10 раз, поток B Печать B 10 раз, нить C Печаты C 10 раз, резьба C требует потока для работы одновременно, а ABC напечатана попеременно 10 раз. Эта проблема может быть легко решена с помощью объекта wait () и notify (). Код заключается в следующем:
/** * ждать использования * @author dreames * @time 2015.3.9 */package com.multithread.wait; открытый класс mythreadprinter2 реализует runnable {private String name; частный объект PREV; личный объект; private mythreadprinter2 (String name, Object Prev, Object Self) {this.name = name; this.prev = prev; this.self = self; } @Override public void run () {int count = 10; while (count> 0) {synchronized (prev) {synchronized (self) {System.out.print (name); считать--; self.notify (); } try {prev.wait (); } catch (прерванное искусство e) {e.printstacktrace (); }}}} public static void main (string [] args) бросает исключение {object a = new Object (); Объект B = new Object (); Объект C = новый объект (); Mythreadprinter2 pa = new mythreadprinter2 ("a", c, a); Mythreadprinter2 pb = new mythreadprinter2 ("b", a, b); Mythreadprinter2 pc = new mythreadprinter2 ("c", b, c); Новый поток (pa) .start (); Thread.sleep (100); // обязательно выполнить новый поток (pb) .start (); Thread.sleep (100); }} Результат вывода:
Abcabcabcabcabcabcabcabcabcabcabcabcc
Давайте сначала объясним свою общую идею. С общей точки зрения, эта проблема представляет собой синхронную операцию пробуждения между тремя потоками. Основная цель состоит в том, чтобы выполнить три потока в ThreadA-> Threadb-> Threadc-> Threada Lop. Чтобы контролировать порядок выполнения потока, должен быть определен порядок пробуждения и ожидания, поэтому каждый поток должен содержать два блокировки объекта одновременно, прежде чем он сможет продолжить выполнение. Заблокировка объекта является Prev, которая является блокировкой объекта, удерживаемым предыдущим потоком. Другой - замок объекта. Основная идея состоит в том, что для управления порядком выполнения вы должны сначала удерживать предыдущую блокировку, то есть предыдущий поток должен выпустить свою собственную блокировку объекта, а затем подать заявку на свою собственную блокировку объекта. Печать, когда оба оба. Затем сначала вызовите self.notify (), чтобы выпустить свою собственную блокировку объекта, разбудить следующий поток ожидания, а затем вызовите Prev.wait (), чтобы выпустить блокировку предварительного объекта, завершить текущий поток и подождать, пока цикл будет разбужден снова. Запустите приведенный выше код, и вы можете обнаружить, что три потока печатают ABC в цикле, в общей сложности 10 раз. Основной процесс работы программы заключается в том, что поток A является первым, кто работает, удерживает блокировки объектов C и A, а затем выпускает замки A и C, и просыпается B. Thread B ожидает блокировки A, затем применяет блокировку B, затем напечатает B, затем выпускает B, блокировки, просыпание C, нить c ожидает блокировки B, затем для блокировки c, затем prints c, c, которые нет. Проблема, но если вы подумаете об этом внимательно, вы обнаружите, что есть проблема, которая является начальным условием. Три нити начинаются в порядке A, B и C. Согласно предыдущим мыслям, A Beakes B, B пробуждает C, C, а затем просыпается A. Однако это предположение зависит от порядка планирования и выполнения потоков в JVM.
Разница между ожиданием и соном
Общие моменты:
1. Все они находятся в многопоточной среде и могут заблокировать указанное количество миллисекундов при вызове программы и возврата.
2. Оба wait () и sleep () могут прервать состояние паузы потока с помощью метода Enterrupt (), так что поток немедленно бросает прерывание.
Если поток A хочет немедленно завершить резьбу B, метод прерывания может быть вызван в экземпляре потока, соответствующий потоке B. Если поток B ждет/сон/соединяется в этот момент, поток B немедленно бросит прерывание и вернет его непосредственно в Catch () {}, чтобы безопасно завершить потоку.
Следует отметить, что прерывавшееэкспением выбрасывается самой нитью изнутри, а не методом Enterrupt (). Когда Enterrupt () вызывается в потоке, если поток выполняет нормальный код, поток вообще не будет выбросить прерывание. Однако после того, как поток входит в wait ()/sleep ()/join (), прерывавшее экспресс будет немедленно брошено.
Различия:
1. Методы класса потока: Sleep (), урожай () и т. Д.
Методы объекта: wat () и уведомление () и т. Д.
2. Каждый объект имеет блокировку для управления синхронным доступом. Синхронизированное ключевое слово может взаимодействовать с блокировкой объекта, чтобы реализовать синхронизацию потока.
Метод сна не высвобождает блокировку, в то время как метод ожидания выпускает блокировку, так что другие потоки могут использовать синхронные управляющие блоки или методы.
3. Подождите, уведомление и уведомление может использоваться только в методах управления синхронизацией или блоков управления синхронизацией, в то время как сон можно использовать где угодно.
4. Сон должен улавливать исключения, в то время как ожидание, уведомление и уведомление не нужно ловить исключения, поэтому самая большая разница между Sleep () и Wait () Методы - это:
Когда сон () спит, держите блокировку объекта и все еще обладает замком;
Когда ожидание () спит, блокировка объекта выпускается.
Тем не менее, Wait () и Sleep () могут оба прервать состояние паузы потока с помощью метода Enterrupt (), так что поток немедленно бросает прерывание (но не рекомендуется использовать этот метод).
Sleep () Метод
Sleep () заставляет текущий поток входить в застойное состояние (блокирует текущий поток), отказываясь от использования чашки, и цель состоит в том, чтобы предотвратить занятие текущего потока занимать ресурсы процессора, полученные одним процессом, чтобы оставить определенное время для выполнения других потоков;
Sleep () является статическим методом класса потока; Следовательно, он не может изменить блокировку машины объекта, поэтому при вызове метода Sleep () в синхронизированном блоке, хотя поток спящий
После истечения времени сна () Sleep () () поток не обязательно выполняется немедленно, потому что другие потоки могут работать и не планируется отказываться от выполнения, если нить не имеет более высокого приоритета.
WAIT () Метод
Метод wait () - это метод в классе объекта; Когда поток выполняет метод wait (), он входит в пул ожидания, связанный с объектом, и в то же время теряет (выпускает) блокировку машины объекта (временно теряет блокировку машины, а время ожидания (длительное время ожидания) также необходимо вернуть блокировку объекта); Другие потоки могут получить к нему доступ;
WAIT () использует уведомление или уведомление или указанное время сна, чтобы разбудить поток в текущем пуле ожидания.
Wiat () должен быть помещен в синхронизированный блок, в противном случае исключение "java.lang.illegalmonitorstateexception" будет брошено во время выполнения программы.
7. Общие термины потока Объяснение
Основной поток: поток, сгенерированный программой вызова JVM Main ().
Текущая тема: это запутанная концепция. Обычно относится к процессу, полученному через Thread.currentThread ().
Фоновая тема: относится к потоке, который предоставляет услуги другим потокам, также известным как поток демона. Поток сбора мусора JVM - это фоновая нить. Разница между пользовательским потоком и потоком демона состоит в том, следует ли ждать, пока основной резьбу заканчивается потоком переднего плана в зависимости от конца основного потока: он относится к потоке, который принимает службу фонового потока. Фактически, фоновые нити переднего плана соединены вместе, как и отношения между марионетом и закулисным манипулятором. Марионетка-это нить переднего плана, а закулисный манипулятор-это фоновая нить. Потоки, созданные потоками переднего плана, также по умолчанию также являются нитью переднего плана. Вы можете использовать методы isdaemon () и setdaemon () для определения и установить, является ли поток фоновым потоком.
Некоторые общие методы классов потоков:
Sleep (): заставьте нить спать в N миллисекундах.
iSalive (): определяет, выживает ли нить.
join (): подождите, пока поток завершится.
ActiveCount (): количество активных потоков в программе.
enumerate (): перечислять потоки в программе.
CurrentThread (): получает текущий поток.
isdaemon (): является ли поток веткой демона.
setDaemon (): установите поток в виде потока демона. (Разница между пользовательским потоком и потоком демона состоит в том, следует ли дождаться окончания основного потока в зависимости от конца основного потока)
setName (): установите имя для потока.
Подождите (): заставьте поток подождать.
notify (): уведомить поток для продолжения работы.
setPriority (): устанавливает приоритет потока.
8. Синхронизация потока
1. Есть две области синхронизированных ключевых слов:
1) Это в экземпляре объекта. Синхронизированный amethod () {} может предотвратить доступ к нескольким потокам добраться до синхронизированного метода этого объекта одновременно (если у объекта есть несколько синхронизированных методов, если один поток обращается к одному из синхронизированных методов, другие потоки не могут получить любые синхронизированные методы в объект одновременно). В настоящее время синхронизированный метод различных экземпляров объекта непрерывно. That is to say, other threads can still access the synchronized method in another object instance of the same class at the same time;
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象;
3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;
Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronized关键字就可以轻松地解决多线程共享数据同步问题。到底如何?还得对synchronized关键字的作用进行深入了解才可定论。
总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
在进一步阐述之前,我们需要明确几点:
A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁而且同步方法很可能还会被其他线程的对象访问。
B.每个对象只有一个锁(lock)与之相关联。
C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
接着来讨论synchronized用到不同地方对代码产生的影响:
假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都可以调用它们。
1. 把synchronized当作函数修饰符时,示例代码如下:
Public synchronized void methodAAA(){//….}这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。
上边的示例代码等同于如下代码:
public void methodAAA(){synchronized (this) // (1){ //…..}}(1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱:(
2.同步块,示例代码如下:
public void method3(SomeObject so) { synchronized(so){ //…..}}这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable{ private byte[] lock = new byte[0]; // 特殊的instance变量Public void methodA(){ synchronized(lock) { //… }}//…..}注:零长度的byte数组对象创建起来将比任何对象都经济查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:
Class Foo{public synchronized static void methodAAA() // 同步的static 函数{//….}public void methodBBB(){ synchronized(Foo.class) // class literal(类名称字面常量)} }代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
记得在《Effective Java》一书中看到过将Foo.class和P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。
可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
九、线程数据传递
在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别。由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。
9.1、通过构造方法传递数据在创建线程时,必须要建立一个Thread类的或其子类的实例。因此,我们不难想到在调用start方法之前通过线程类的构造方法将数据传入线程。并将传入的数据使用类变量保存起来,以便线程使用(其实就是在run方法中使用)。下面的代码演示了如何通过构造方法来传递数据:
package mythread; public class MyThread1 extends Thread { private String name; public MyThread1(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { Thread thread = new MyThread1("world"); Thread.Start (); }}由于这种方法是在创建线程对象的同时传递数据的,因此,在线程运行之前这些数据就就已经到位了,这样就不会造成数据在线程运行后才传入的现象。如果要传递更复杂的数据,可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。由于Java没有默认参数,要想实现类似默认参数的效果,就得使用重载,这样不但使构造方法本身过于复杂,又会使构造方法在数量上大增。因此,要想避免这种情况,就得通过类方法或类变量来传递数据。
9.2. Pass data through variables and methods
向对象中传入数据一般有两次机会,第一次机会是在建立对象时通过构造方法将数据传入,另外一次机会就是在类中定义一系列的public的方法或变量(也可称之为字段)。然后在建立完对象后,通过对象实例逐个赋值。下面的代码是对MyThread1类的改版,使用了一个setName方法来设置name变量:
package mythread; public class MyThread2 implements Runnable { private String name; public void setName(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { MyThread2 myThread = new MyThread2(); myThread.setName("world"); Thread thread = new Thread(myThread); Thread.Start (); }} 9.3、通过回调函数传递数据
上面讨论的两种向线程中传递数据的方法是最常用的。但这两种方法都是main方法中主动将数据传入线程类的。这对于线程来说,是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据,如在下面代码的run方法中产生了3个随机数,然后通过Work类的process方法求这三个随机数的和,并通过Data类的value将结果返回。从这个例子可以看出,在返回value之前,必须要得到三个随机数。也就是说,这个value是无法事先就传入线程类的。
package mythread; class Data { public int value = 0; } class Work { public void process(Data data, Integer numbers) { for (int n : numbers) { data.value += n; } } } public class MyThread3 extends Thread { private Work work; public MyThread3(Work work) { this.work = work; } public void run() { java.util.Random random = new java.util.Random(); Data data = new Data(); int n1 = random.nextInt(1000); int n2 = random.nextInt(2000); int n3 = random.nextInt(3000); work.process(data, n1, n2, n3); // Use the callback function System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+" + String.valueOf(n3) + "=" + data.value); } public static void main(String[] args) { Thread thread = new MyThread3(new Work()); Thread.Start (); }}The above is a detailed explanation of Java multi-threading. I hope it can help you learn this part of the knowledge. Спасибо за поддержку этого сайта!