Java — это многопоточный язык программирования, что означает, что мы можем разрабатывать многопоточные программы с использованием Java. Многопоточная программа содержит две или более частей, которые могут работать одновременно, и каждая из них может одновременно выполнять разные задачи, оптимально используя доступные ресурсы, особенно если на вашем компьютере установлено несколько процессоров.
По определению многозадачность — это когда несколько процессов совместно используют общие ресурсы обработки, такие как ЦП. Многопоточность расширяет идею многозадачности в приложениях, где вы можете разделить определенные операции в рамках одного приложения на отдельные потоки. Каждый из потоков может работать параллельно. ОС делит время обработки не только между различными приложениями, но и между каждым потоком в приложении.
Многопоточность позволяет вам писать так, чтобы несколько действий могли выполняться одновременно в одной и той же программе.
Жизненный цикл потока
Нить проходит через различные стадии своего жизненного цикла. Например, поток рождается, запускается, запускается, а затем умирает. Следующая диаграмма показывает полный жизненный цикл потока.
Ниже приведены этапы жизненного цикла —
-
Новый — новый поток начинает свой жизненный цикл в новом состоянии. Он остается в этом состоянии, пока программа не запустит поток. Это также упоминается как прирожденная нить .
-
Runnable — после запуска только что созданного потока поток становится работоспособным. Поток в этом состоянии считается выполняющим свою задачу.
-
Ожидание — иногда поток переходит в состояние ожидания, пока поток ожидает, пока другой поток выполнит задачу. Поток возвращается в состояние выполнения только тогда, когда другой поток сигнализирует ожидающему потоку о продолжении выполнения.
-
Временное ожидание — работающий поток может войти в состояние ожидания по времени в течение определенного интервала времени. Поток в этом состоянии возвращается в работоспособное состояние, когда истекает этот временной интервал или когда происходит ожидаемое событие.
-
Завершено (Dead) — работающий поток входит в завершенное состояние, когда он завершает свою задачу или иным образом завершает свою работу.
Новый — новый поток начинает свой жизненный цикл в новом состоянии. Он остается в этом состоянии, пока программа не запустит поток. Это также упоминается как прирожденная нить .
Runnable — после запуска только что созданного потока поток становится работоспособным. Поток в этом состоянии считается выполняющим свою задачу.
Ожидание — иногда поток переходит в состояние ожидания, пока поток ожидает, пока другой поток выполнит задачу. Поток возвращается в состояние выполнения только тогда, когда другой поток сигнализирует ожидающему потоку о продолжении выполнения.
Временное ожидание — работающий поток может войти в состояние ожидания по времени в течение определенного интервала времени. Поток в этом состоянии возвращается в работоспособное состояние, когда истекает этот временной интервал или когда происходит ожидаемое событие.
Завершено (Dead) — работающий поток входит в завершенное состояние, когда он завершает свою задачу или иным образом завершает свою работу.
Приоритеты потоков
Каждый поток Java имеет приоритет, который помогает операционной системе определять порядок, в котором запланированы потоки.
Приоритеты потоков Java находятся в диапазоне между MIN_PRIORITY (константа 1) и MAX_PRIORITY (константа 10). По умолчанию каждому потоку присваивается приоритет NORM_PRIORITY (константа 5).
Потоки с более высоким приоритетом более важны для программы и должны выделять процессорное время перед потоками с более низким приоритетом. Однако приоритеты потоков не могут гарантировать порядок выполнения потоков и очень сильно зависят от платформы.
Создайте поток, реализуя работающий интерфейс
Если ваш класс предназначен для выполнения в виде потока, вы можете добиться этого, реализуя интерфейс Runnable . Вам нужно будет выполнить три основных шага —
Шаг 1
В качестве первого шага вам необходимо реализовать метод run (), предоставляемый интерфейсом Runnable . Этот метод обеспечивает точку входа для потока, и вы поместите всю свою бизнес-логику в этот метод. Ниже приведен простой синтаксис метода run ():
public void run( )
Шаг 2
На втором этапе вы создадите экземпляр объекта Thread, используя следующий конструктор:
Thread(Runnable threadObj, String threadName);
Где threadObj — это экземпляр класса, который реализует интерфейс Runnable, а threadName — это имя, данное новому потоку.
Шаг 3
Как только объект Thread создан, вы можете запустить его, вызвав метод start () , который выполняет вызов метода run (). Ниже приведен простой синтаксис метода start ():
void start();
пример
Вот пример, который создает новый поток и запускает его:
class RunnableDemo implements Runnable { private Thread t; private String threadName; RunnableDemo( String name) { threadName = name; System.out.println("Creating " + threadName ); } public void run() { System.out.println("Running " + threadName ); try { for(int i = 4; i > 0; i--) { System.out.println("Thread: " + threadName + ", " + i); // Let the thread sleep for a while. Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("Thread " + threadName + " interrupted."); } System.out.println("Thread " + threadName + " exiting."); } public void start () { System.out.println("Starting " + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class TestThread { public static void main(String args[]) { RunnableDemo R1 = new RunnableDemo( "Thread-1"); R1.start(); RunnableDemo R2 = new RunnableDemo( "Thread-2"); R2.start(); } }
Это даст следующий результат —
Выход
Creating Thread-1 Starting Thread-1 Creating Thread-2 Starting Thread-2 Running Thread-1 Thread: Thread-1, 4 Running Thread-2 Thread: Thread-2, 4 Thread: Thread-1, 3 Thread: Thread-2, 3 Thread: Thread-1, 2 Thread: Thread-2, 2 Thread: Thread-1, 1 Thread: Thread-2, 1 Thread Thread-1 exiting. Thread Thread-2 exiting.
Создать поток, расширяя класс потока
Второй способ создания потока — это создание нового класса, расширяющего класс Thread, с помощью следующих двух простых шагов. Этот подход обеспечивает большую гибкость в обработке нескольких потоков, созданных с использованием доступных методов в классе Thread.
Шаг 1
Вам нужно будет переопределить метод run (), доступный в классе Thread. Этот метод обеспечивает точку входа для потока, и вы поместите всю свою бизнес-логику в этот метод. Ниже приведен простой синтаксис метода run ():
public void run( )
Шаг 2
После создания объекта Thread его можно запустить, вызвав метод start () , который выполняет вызов метода run (). Ниже приведен простой синтаксис метода start ():
void start( );
пример
Вот предыдущая программа, переписанная для расширения темы —
class ThreadDemo extends Thread { private Thread t; private String threadName; ThreadDemo( String name) { threadName = name; System.out.println("Creating " + threadName ); } public void run() { System.out.println("Running " + threadName ); try { for(int i = 4; i > 0; i--) { System.out.println("Thread: " + threadName + ", " + i); // Let the thread sleep for a while. Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("Thread " + threadName + " interrupted."); } System.out.println("Thread " + threadName + " exiting."); } public void start () { System.out.println("Starting " + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class TestThread { public static void main(String args[]) { ThreadDemo T1 = new ThreadDemo( "Thread-1"); T1.start(); ThreadDemo T2 = new ThreadDemo( "Thread-2"); T2.start(); } }
Это даст следующий результат —
Выход
Creating Thread-1 Starting Thread-1 Creating Thread-2 Starting Thread-2 Running Thread-1 Thread: Thread-1, 4 Running Thread-2 Thread: Thread-2, 4 Thread: Thread-1, 3 Thread: Thread-2, 3 Thread: Thread-1, 2 Thread: Thread-2, 2 Thread: Thread-1, 1 Thread: Thread-2, 1 Thread Thread-1 exiting. Thread Thread-2 exiting.
Методы потока
Ниже приведен список важных методов, доступных в классе Thread.
Sr.No. | Метод и описание |
---|---|
1 |
public void start () Запускает поток по отдельному пути выполнения, затем вызывает метод run () для этого объекта Thread. |
2 |
public void run () Если этот объект Thread был создан с использованием отдельной цели Runnable, метод run () вызывается для этого объекта Runnable. |
3 |
публичный финал void setName (имя строки) Изменяет имя объекта Thread. Существует также метод getName () для получения имени. |
4 |
public final void setPriority (int priority) Устанавливает приоритет этого объекта Thread. Возможные значения от 1 до 10. |
5 |
публичный финал void setDaemon (логическое значение включено) Параметр true обозначает этот поток как поток демона. |
6 |
публичное окончание void join (длинный миллисек) Текущий поток вызывает этот метод во втором потоке, в результате чего текущий поток блокируется до тех пор, пока второй поток не завершится или не пройдет указанное количество миллисекунд. |
7 |
public void interrupt () Прерывает этот поток, заставляя его продолжить выполнение, если он был заблокирован по какой-либо причине. |
8 |
открытый финальный логический isAlive () Возвращает true, если поток активен, что происходит в любое время после запуска потока, но до его завершения. |
public void start ()
Запускает поток по отдельному пути выполнения, затем вызывает метод run () для этого объекта Thread.
public void run ()
Если этот объект Thread был создан с использованием отдельной цели Runnable, метод run () вызывается для этого объекта Runnable.
публичный финал void setName (имя строки)
Изменяет имя объекта Thread. Существует также метод getName () для получения имени.
public final void setPriority (int priority)
Устанавливает приоритет этого объекта Thread. Возможные значения от 1 до 10.
публичный финал void setDaemon (логическое значение включено)
Параметр true обозначает этот поток как поток демона.
публичное окончание void join (длинный миллисек)
Текущий поток вызывает этот метод во втором потоке, в результате чего текущий поток блокируется до тех пор, пока второй поток не завершится или не пройдет указанное количество миллисекунд.
public void interrupt ()
Прерывает этот поток, заставляя его продолжить выполнение, если он был заблокирован по какой-либо причине.
открытый финальный логический isAlive ()
Возвращает true, если поток активен, что происходит в любое время после запуска потока, но до его завершения.
Предыдущие методы вызываются для определенного объекта Thread. Следующие методы в классе Thread являются статическими. Вызов одного из статических методов выполняет операцию с текущим запущенным потоком.
Sr.No. | Метод и описание |
---|---|
1 |
public static void yield () Заставляет текущий запущенный поток уступать любым другим потокам с тем же приоритетом, которые ожидают, чтобы быть запланированными. |
2 |
Публичный статический бессильный сон (длинный миллисек) Заставляет текущий запущенный поток блокироваться как минимум на указанное количество миллисекунд. |
3 |
public static boolean holdLock (Объект x) Возвращает true, если текущий поток удерживает блокировку данного объекта. |
4 |
публичный статический поток currentThread () Возвращает ссылку на текущий запущенный поток, который является потоком, который вызывает этот метод. |
5 |
public static void dumpStack () Печатает трассировку стека для текущего запущенного потока, что полезно при отладке многопоточного приложения. |
public static void yield ()
Заставляет текущий запущенный поток уступать любым другим потокам с тем же приоритетом, которые ожидают, чтобы быть запланированными.
Публичный статический бессильный сон (длинный миллисек)
Заставляет текущий запущенный поток блокироваться как минимум на указанное количество миллисекунд.
public static boolean holdLock (Объект x)
Возвращает true, если текущий поток удерживает блокировку данного объекта.
публичный статический поток currentThread ()
Возвращает ссылку на текущий запущенный поток, который является потоком, который вызывает этот метод.
public static void dumpStack ()
Печатает трассировку стека для текущего запущенного потока, что полезно при отладке многопоточного приложения.
пример
Следующая программа ThreadClassDemo демонстрирует некоторые из этих методов класса Thread. Рассмотрим класс DisplayMessage, который реализует Runnable —
// File Name : DisplayMessage.java // Create a thread to implement Runnable public class DisplayMessage implements Runnable { private String message; public DisplayMessage(String message) { this.message = message; } public void run() { while(true) { System.out.println(message); } } }
Ниже приведен еще один класс, расширяющий класс Thread.
// File Name : GuessANumber.java // Create a thread to extentd Thread public class GuessANumber extends Thread { private int number; public GuessANumber(int number) { this.number = number; } public void run() { int counter = 0; int guess = 0; do { guess = (int) (Math.random() * 100 + 1); System.out.println(this.getName() + " guesses " + guess); counter++; } while(guess != number); System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**"); } }
Ниже приводится основная программа, в которой используются определенные выше классы.
// File Name : ThreadClassDemo.java public class ThreadClassDemo { public static void main(String [] args) { Runnable hello = new DisplayMessage("Hello"); Thread thread1 = new Thread(hello); thread1.setDaemon(true); thread1.setName("hello"); System.out.println("Starting hello thread..."); thread1.start(); Runnable bye = new DisplayMessage("Goodbye"); Thread thread2 = new Thread(bye); thread2.setPriority(Thread.MIN_PRIORITY); thread2.setDaemon(true); System.out.println("Starting goodbye thread..."); thread2.start(); System.out.println("Starting thread3..."); Thread thread3 = new GuessANumber(27); thread3.start(); try { thread3.join(); } catch (InterruptedException e) { System.out.println("Thread interrupted."); } System.out.println("Starting thread4..."); Thread thread4 = new GuessANumber(75); thread4.start(); System.out.println("main() is ending..."); } }
Это даст следующий результат. Вы можете попробовать этот пример снова и снова, и каждый раз вы получите другой результат.
Выход
Starting hello thread... Starting goodbye thread... Hello Hello Hello Hello Hello Hello Goodbye Goodbye Goodbye Goodbye Goodbye .......
Основные многопоточные концепции Java
При многопоточном программировании на Java вам понадобятся следующие концепции:
Что такое синхронизация потоков?