1. Процессы и потоки
1. Что такое процесс?
Узкое определение: процесс - это экземпляр компьютерной программы, которая выполняется.
Общее определение: процесс - это работающая деятельность программы с определенными независимыми функциями, касающимися определенного набора данных. Это основная единица динамического выполнения операционной системы. В традиционных операционных системах процессы являются как основными единицами распределения, так и основными единицами выполнения.
2. Что такое поток?
Потоки, иногда называемые легкими процессами (LWP), являются самыми маленькими единицами потока выполнения программы. Стандартный поток состоит из идентификатора потока, текущего указателя инструкции (ПК), набора регистров и стека. Кроме того, поток является объектом в процессе и является основной единицей, которая независимо запланирована и отправляется системой. Сам поток не владеет системными ресурсами, но имеет лишь несколько важных ресурсов во время работы, но он может поделиться всеми ресурсами, принадлежащими процессу с другими потоками, принадлежащими к тому же процессу.
3. В чем разница между процессом и потоком?
Основное различие между процессами и потоками заключается в том, что они представляют собой различные методы управления ресурсами операционной системы.
Процесс имеет независимое адресное пространство. После сбоя процесса он не повлияет на другие процессы в защищенном режиме, а поток - это просто другой путь выполнения в процессе.
В потоках есть свой собственный стек и локальные переменные, но между потоками нет отдельного адресного пространства. Если поток умирает, это означает, что весь процесс умирает. Таким образом, многопроцессорные программы более надежны, чем многопоточные программы, но при переключении процессов они потребляют больше ресурсов и являются менее эффективными. Однако для некоторых параллельных операций, которые требуют одновременных операций, которые требуют обмена определенными переменными, они могут использовать только потоки, а не процессы.
Короче говоря, разница между потоком и процессом составляет:
(1) Программа имеет по крайней мере один процесс, а процесс имеет по крайней мере один поток;
(2) Шкала нитей деления меньше, чем в процессе, что делает параллелизм многопоточных программ высоким.
(3) Процесс имеет независимые единицы памяти во время выполнения, а несколько потоков обмениваются памятью, что значительно повышает эффективность работы программы.
(4) Существует разница между потоками и процессами во время выполнения. Каждый независимый поток имеет запись для выполнения программы, последовательность выполнения и выход для программы. Тем не менее, потоки не могут быть выполнены независимо и должны существовать в приложении, а приложение предоставляется несколько элементов выполнения потока.
(5) С логической точки зрения значение мультипотчики заключается в том, что в приложении можно выполнить несколько частей выполнения одновременно. Тем не менее, операционная система не рассматривает несколько потоков как несколько независимых приложений для реализации планирования процессов, управления и распределения ресурсов.
Это важное различие между процессом и потоком.
2. Жизненный цикл потока и пять основных состояний
В темах Java есть пять основных состояний:
(1) Новое состояние (новое): когда создается пара объектов потока, оно входит в новое состояние, такое как: Thread T = New Mythread ();
(2) Готовное состояние (запустить): когда метод start () объекта потока (t.start ();), поток входит в состояние готового состояния. Поток в состоянии готовности просто означает, что поток готов и ждет, пока ЦП не запланирует выполнение в любое время, а не в том, что поток будет выполняться сразу после выполнения t.start ();
(3) Состояние управления: когда процессор начинает планировать потоки в состоянии готового состояния, поток может быть по -настоящему выполнен, то есть он входит в состояние управления. Примечание. Готовное состояние - единственная запись в управляемом состоянии, то есть, если поток хочет ввести в состояние выполнения для выполнения, оно должно сначала быть в состоянии готового состояния;
(4) Заблокированное состояние: по какой -то причине поток в управляемом состоянии временно отказывается от использования ЦП и прекращает выполнение. В настоящее время он входит в состояние блокировки. У него не будет возможности снова вызвать процессор, чтобы войти в состояние бега. Согласно причинам блокировки, состояния блокировки можно разделить на три типа:
① Ожидание блокировки: поток в состоянии работающего состояния выполняет метод wait (), чтобы ввод потока вошел в ожидание состояния блокировки;
② Синхронизированное блокирование: нить не может получить синхронизированную блокировку синхронизации (потому что блокировка занята другими потоками), и он входит в синхронизированное состояние блокировки;
③ другое блокирование: при вызове потока Sleep () или join () или отправки запроса ввода/вывода, поток войдет в состояние блокировки. Когда состояние Sleep () истекло, join () ждал, пока поток заканчивается или истекло, или обработка ввода-вывода была завершена, поток снова въехал в состояние готового состояния.
(5) Мертвое состояние: поток закончил выполнение или выход метода run () из -за исключения, а поток заканчивает свой жизненный цикл.
3. Реализация многопоточной чтения Java
В Java, если вы хотите реализовать многопоточную программу, вы должны полагаться на основной класс потока (например, концепция основного класса, которая представляет основной класс потока), но основной класс этого потока должен иметь некоторые особые требования при его определении. Этот класс может наследовать класс потока или реализовать запускаемый интерфейс, чтобы завершить определение.
1. Унаследовать класс потоков для реализации многопоточного
java.lang.thread - это класс, ответственный за операции потока. Любой класс может стать основным классом потока, если он наследует класс потока. Поскольку это основной класс, он должен иметь свои методы использования, а основной метод, запускаемый потоком, должен перезаписать метод run () в классе потока.
Определите класс тела нить:
Класс Mythread Extends Thread {// Основной класс Thread Private String Title; public mythread (string title) {this.title = title; } @Override public void run () {// Основной метод потока для (int x = 0; x <10; x ++) {System.out.println (this.title + "run, x =" + x); }}}Теперь, когда есть класс потоков, и в нем есть соответствующие методы работы, объект должен быть сгенерирован, и следует вызвать методы внутри, поэтому записывается следующая программа:
открытый класс testDemo {public static void main (string [] args) {mythread mt1 = new Mythread ("Thread a"); Mythread mt2 = new Mythread ("Thread B"); Mythread mt3 = new Mythread ("Thread C"); mt1.run (); mt2.run (); mt3.run (); }Результаты работы:
Потока A пробегает, x = 0
Потока A пробегает, x = 1
Потока A пробегает, x = 2
Потока A пробегает, x = 3
Потока A пробегает, x = 4
Потока A пробегает, x = 5
Потока A пробегает, x = 6
Потока A пробегает, x = 7
Потока A пробегает, x = 8
Потока A пробегает, x = 9
Поток B работает, x = 0
Поток B работает, x = 1
Поток B работает, x = 2
Поток B работает, x = 3
Поток B работает, x = 4
Поток B работает, x = 5
Поток B работает, x = 6
Поток B работает, x = 7
Поток B работает, x = 8
Поток B работает, x = 9
Поток C работает, x = 0
Поток C работает, x = 1
Поток C работает, x = 2
Поток C работает, x = 3
Поток C работает, x = 4
Поток C работает, x = 5
Поток C работает, x = 6
Поток C работает, x = 7
Поток C работает, x = 8
Поток C работает, x = 9
Мы обнаружили, что вышеуказанные операции на самом деле не запускают многопоточное, потому что выполнение нескольких потоков должно быть попеременно запускать, и в настоящее время оно выполняется последовательно, и код каждого объекта будет продолжать выполняться вниз после выполнения кода каждого объекта.
Если вы хотите по-настоящему начать многопоточное в программе, вы должны полагаться на метод класса потока: public void start (), что означает, что вы действительно начинаете многопоточное. После вызова этого метода метод run () будет косвенно вызван:
открытый класс testDemo {public static void main (string [] args) {mythread mt1 = new Mythread ("Thread a"); Mythread mt2 = new Mythread ("Thread B"); Mythread mt3 = new Mythread ("Thread C"); mt1.start (); mt2.start (); mt3.start (); }}Результаты работы:
Поток C работает, x = 0
Потока A пробегает, x = 0
Поток B работает, x = 0
Потока A пробегает, x = 1
Поток C работает, x = 1
Потока A пробегает, x = 2
Поток B работает, x = 1
Потока A пробегает, x = 3
Потока A пробегает, x = 4
Потока A пробегает, x = 5
Поток C работает, x = 2
Поток C работает, x = 3
Поток C работает, x = 4
Поток C работает, x = 5
Поток C работает, x = 6
Поток C работает, x = 7
Поток C работает, x = 8
Поток C работает, x = 9
Потока A пробегает, x = 6
Потока A пробегает, x = 7
Потока A пробегает, x = 8
Потока A пробегает, x = 9
Поток B работает, x = 2
Поток B работает, x = 3
Поток B работает, x = 4
Поток B работает, x = 5
Поток B работает, x = 6
Поток B работает, x = 7
Поток B работает, x = 8
Поток B работает, x = 9
В настоящее время вы можете обнаружить, что несколько потоков попеременно выполняются друг с другом, но результаты каждого выполнения различны. Через приведенный выше код мы можем сделать вывод: если вы хотите запустить поток, вы должны полагаться на метод начала () класса потока для выполнения. После запуска потока метод run () будет вызван по умолчанию.
После вызова метода start () произошла серия сложных вещей:
(1) запустить новый поток выполнения (с новым стеком вызовов);
(2) поток переносится из нового состояния в бегемое состояние;
(3) Когда поток получает возможность выполнить, его метод Target Run () будет работать.
Примечание. Для Java метод run () не имеет ничего особенного. Как и метод Main (), это просто означает, что новый поток знает имя метода (и подпись) вызова. Следовательно, законно вызвать метод запуска на запускаемой или потоке, но не запускает новый поток.
Объяснение: Почему вы должны вызовать start () вместо непосредственного вызова run () при запуске потока?
Мы обнаружили, что после вызова start () он фактически выполняет метод переопределенного run (), так почему бы не вызовать метод run () напрямую? Чтобы объяснить эту проблему, откройте исходный код класса потока и соблюдайте определение метода start ():
public Synchronized void start () {if (ThreadStatus! = 0) добавить новый allodalThreadStateException (); Group.Add (это); логический запуск = false; try {start0 (); начал = true; } наконец {try {if (! Запустил) {Group.ThreadStartFailed (this); }} catch (Throwable Ignore) {}}} private vand void start0 ();Откройте исходный код этого метода, и сначала вы обнаружите, что метод будет выбросить исключение «нелегальной чреточниц». Вообще говоря, если метод использует бросок, чтобы бросить объект исключения, то это исключение следует поймать, используя Try ... Catch или брошен с использованием бросков на объявление метода, но в этой области ничего нет. Почему? Потому что этот класс исключений принадлежит подклассу исключения времени выполнения (Runtimeexception):
java.lang.object
|- java.lang.frowable
|- java.lang.exception
|- java.lang.runtimeexception
|- java.lang.illegalargumentException
|- java.lang.illegalthreadstateException
Это исключение будет брошено, когда объект потока неоднократно запускается, то есть объект потока можно запустить только один раз.
Одной из наиболее важных частей метода start () является метод start0 (), и этот метод использует нативное определение ключевого слова.
Нативное ключевое слово относится к нативному интерфейсу Java, то есть Java используется для вызова функций функций нативной операционной системы для завершения некоторых специальных операций. Такая разработка кода почти редко наблюдается в Java, потому что самой большой особенностью Java является портативность. Если программа может использоваться только в фиксированной операционной системе, портативность будет полностью потеряна, поэтому эта операция обычно не используется.
Реализация многопоточного чтения должна требовать поддержки операционной системы. Затем приведенный выше метод start0 () на самом деле очень похож на абстрактный метод без тела метода. Этот кузов метода передается JVM для реализации, то есть JVM в Windows может использовать метод A для реализации start0 (), в то время как JVM в Linux может использовать метод B для реализации start0 (), но при вызове он не будет заботиться о конкретном методе реализации метода start0 (), но только забота о результатах окончательной операции и передает JVM, чтобы соответствовать различным операционным системам.
Следовательно, в многопоточных операциях, используя метод start () для запуска многопоточных операций, требуют вызовов функций операционной системы.
2. Реализуйте запускаемый интерфейс для реализации многопоточного
Использование класса потоков действительно может облегчить реализацию многопоточной реализации, но самым большим недостатком этого метода является проблема единственного наследования. С этой целью, выполняемый интерфейс также можно использовать в Java для реализации многопоточного. Определение этого интерфейса заключается в следующем:
Public Interface Runnable {public void run ();}Реализуйте многопоточное интерфейс:
класс Mythread реализует Runnable {// Основной класс потока public mythread (string title) {this.title = title; } @Override public void run () {// Основной метод потока для (int x = 0; x <10; x ++) {System.out.println (this.title + "run, x =" + x); }}}Это не сильно отличается от предыдущего способа наследования классов потоков, но одно преимущество заключается в том, что он избегает ограничения единого наследования.
Но проблема здесь. Как упоминалось ранее, если вы хотите начать многопоточное, вам нужно полагаться на метод начала () класса потока. Когда вы наследуете класс потоков, вы можете непосредственно наследовать этот метод и использовать его. Но теперь вы реализуете запускаемый интерфейс. Без этого метода вы можете унаследовать его. Что вы должны делать?
Чтобы решить эту проблему, вам все еще нужно полагаться на класс потоков, чтобы завершить ее. Конструктор определяется в классе потока для получения объекта запускаемого интерфейса:
публичная тема (выполняемая цель);
Запустить многопоточное чтение с использованием класса потоков:
открытый класс testDemo {public static void main (string [] args) выбрасывает исключение {mythread mt1 = new Mythread ("Thread A"); Mythread mt2 = new Mythread ("Thread B"); Mythread mt3 = new Mythread ("Thread C"); Новый поток (mt1) .start (); Новый поток (mt2) .start (); Новый поток (mt3) .start (); }}Результаты работы:
Потока A пробегает, x = 0
Поток B работает, x = 0
Поток B работает, x = 1
Поток C работает, x = 0
Поток B работает, x = 2
Потока A пробегает, x = 1
Поток B работает, x = 3
Поток C работает, x = 1
Поток C работает, x = 2
Поток B работает, x = 4
Поток B работает, x = 5
Потока A пробегает, x = 2
Потока A пробегает, x = 3
Потока A пробегает, x = 4
Потока A пробегает, x = 5
Потока A пробегает, x = 6
Потока A пробегает, x = 7
Потока A пробегает, x = 8
Потока A пробегает, x = 9
Поток B работает, x = 6
Поток B работает, x = 7
Поток B работает, x = 8
Поток B работает, x = 9
Поток C работает, x = 3
Поток C работает, x = 4
Поток C работает, x = 5
Поток C работает, x = 6
Поток C работает, x = 7
Поток C работает, x = 8
Поток C работает, x = 9
В настоящее время достигается не только многопоточный стартап, но и никаких ограничений наследования.
3. Третий метод реализации мультипотового: используйте Callable Interface для реализации многопоточного
Многопользовательский интерфейс с использованием запускаемого интерфейса может избежать ограничения единого наследования, но есть проблема, что метод run () в интерфейсе запускается, не может вернуть результат работы. Чтобы решить эту проблему, предоставлен новый интерфейс: Callable Interface (java.util.concurrent.callable).
Общедоступный интерфейс Callable <v> {public v Call () бросает исключение;}После выполнения метода Call () в интерфейсе Callable будет возвращен результат. Тип возвращаемого результата определяется дженериками на выявленном интерфейсе.
Специальная операция реализации каллируемого интерфейса для реализации многопоточного
Создать класс реализации разбираемого интерфейса и реализовать метод Clall (); Затем используйте класс FutureTask, чтобы обернуть объект класса Callable реализации, и используйте этот объект FutureTask в качестве цели объекта потока для создания потока.
Определите класс тела потока:
Import java.util.concurrent.callable; класс Mythread реализует Callable <string> {private int ticket = 10; @Override public String call () Throws Exception {for (int i = 0; i <20; i ++) {if (this.ticket> 0) {System.out.println («Продает билеты, оставшееся количество голосов:+this.ticket -); }} return "Билеты были распроданы"; }}Класс потоков напрямую не поддерживает выставленный интерфейс. После JDK1.5 предоставляется класс:
java.util.concurrent.futuretask <v>
Этот класс в основном отвечает за работу объекта Callable Interface. Его структура определения следующей:
Общедоступный класс FutureTask <v>
расширяет объект
Реализует RunnableFurture <v>
Интерфейс RunnableFurte имеет следующее определение:
Публичный интерфейс RunnableFurte <v>
Распространяет бегство, будущее <v>
Следующий конструктор определяется в классе FutureTask:
Public FutureTask (Callible <v> Callable)
Теперь, наконец, вы можете получить Callable Interface Object через класс FutureTask. Цель получения - получить результат возврата метода Call ().
Из приведенного выше анализа мы можем найти:
Класс FutureTask может получать Callable Interface Objects, а класс FutureTask реализует интерфейс RunnableFurture, который наследует запускаемый интерфейс.
Итак, мы можем начать многопоточности так:
открытый класс testDemo {public static void main (string [] args) бросает исключение {mythread mt1 = new mythread (); Mythread mt2 = new mythread (); FutureTask <string> task1 = new FutureTask <string> (mt1); // get call () Метод вернуть результат FutureTask <string> task2 = new FutureSk <string> (mt2); // get call (), чтобы вернуть результат // futureTask - это подкласс интерфейса. Вы можете использовать конструкцию класса потоков для получения объектов задачи нового потока (task1) .start (); новый поток (Task2) .start (); // После завершения многопоточного выполнения вы можете использовать метод get () в будущем родительском интерфейсе FutureTask для получения системы выполнения. System.out.println ("Результат возврата потока 2:"+task2.get ()); }}Результаты работы:
Продавать билеты, оставшееся количество голосов составляет 10
Продавать билеты, оставшееся количество голосов составляет 10
Продавать билеты, оставшееся количество голосов - 9
Продавать билеты, оставшееся количество голосов - 8
Продавать билеты, оставшееся количество голосов - 7
Продавать билеты, оставшееся количество голосов - 9
Продавать билеты, оставшееся количество голосов - 6
Продавать билеты, оставшееся количество голосов - 8
Продавать билеты, оставшееся количество голосов составляет 5
Продавать билеты, оставшееся количество голосов - 7
Продавать билеты, оставшееся количество голосов - 4
Продавать билеты, оставшееся количество голосов - 6
Продавать билеты, оставшееся количество голосов - 3
Продавать билеты, оставшееся количество голосов составляет 5
Продавать билеты, оставшееся количество голосов составляет 2
Продавать билеты, оставшееся количество голосов - 4
Продавать билеты, оставшееся количество голосов составляет 1
Продавать билеты, оставшееся количество голосов - 3
Продавать билеты, оставшееся количество голосов составляет 2
Продавать билеты, оставшееся количество голосов составляет 1
Результат возврата потока 1: билет был распродан. Результат возврата потока 2: билет был распродан.
краткое содержание:
Приведенное выше объясняется три способа реализации многопоточного чтения. Для запуска потока все они называются методом start () объекта потока. Важно отметить, что метод start () не может быть вызван дважды на одном объекте потока.