Java - многопоточность
Java - это многопоточный язык программирования, что означает, что мы можем разрабатывать многопоточные программы с использованием Java. Многопоточная программа состоит из двух или более частей, которые могут выполняться одновременно, и каждая часть может обрабатывать разные задачи одновременно, оптимально используя доступные ресурсы, особенно когда ваш компьютер имеет несколько процессоров.
По определению, многозадачность - это когда несколько процессов совместно используют общие ресурсы обработки, такие как ЦП. Многопоточность расширяет идею многозадачности на приложения, где вы можете разделить определенные операции в рамках одного приложения на отдельные потоки. Каждый из потоков может работать параллельно. ОС распределяет время обработки не только между различными приложениями, но и между каждым потоком в приложении.
Многопоточность позволяет писать таким образом, чтобы несколько действий могли выполняться одновременно в одной программе.
Жизненный цикл потока
В своем жизненном цикле поток проходит различные стадии. Например, поток создается, запускается, выполняется и затем умирает. На следующей диаграмме показан полный жизненный цикл потока.
Ниже приведены этапы жизненного цикла -
New- Новый поток начинает свой жизненный цикл в новом состоянии. Он остается в этом состоянии до тех пор, пока программа не запустит поток. Его также называютborn thread.
Runnable- После запуска вновь созданного потока он становится работоспособным. Считается, что поток в этом состоянии выполняет свою задачу.
Waiting- Иногда поток переходит в состояние ожидания, пока поток ожидает, пока другой поток выполнит задачу. Поток переходит обратно в рабочее состояние только тогда, когда другой поток сигнализирует ожидающему потоку продолжить выполнение.
Timed Waiting- Выполняемый поток может войти в состояние ожидания по времени в течение указанного интервала времени. Поток в этом состоянии переходит обратно в рабочее состояние, когда истекает этот временной интервал или когда происходит событие, которого он ожидает.
Terminated (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.
Создайте поток, расширив класс потока
Второй способ создать поток - создать новый класс, расширяющий Threadclass, используя следующие два простых шага. Этот подход обеспечивает большую гибкость в обработке нескольких потоков, созданных с использованием доступных методов в классе 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, для этого объекта Runnable вызывается метод run (). |
3 | public final void setName(String name) Изменяет имя объекта Thread. Также существует метод getName () для получения имени. |
4 | public final void setPriority(int priority) Устанавливает приоритет этого объекта Thread. Возможные значения от 1 до 10. |
5 | public final void setDaemon(boolean on) Параметр true обозначает этот поток как поток демона. |
6 | public final void join(long millisec) Текущий поток вызывает этот метод во втором потоке, в результате чего текущий поток блокируется, пока второй поток не завершится или не пройдет заданное количество миллисекунд. |
7 | public void interrupt() Прерывает этот поток, заставляя его продолжить выполнение, если он был заблокирован по какой-либо причине. |
8 | public final boolean isAlive() Возвращает true, если поток активен, то есть в любое время после запуска потока, но до его завершения. |
Предыдущие методы вызываются для конкретного объекта Thread. Следующие методы в классе Thread являются статическими. Вызов одного из статических методов выполняет операцию в текущем запущенном потоке.
Sr. No. | Метод и описание |
---|---|
1 | public static void yield() Заставляет текущий выполняющийся поток уступить место любым другим потокам с таким же приоритетом, которые ожидают своего планирования. |
2 | public static void sleep(long millisec) Заставляет текущий запущенный поток блокироваться по крайней мере на указанное количество миллисекунд. |
3 | public static boolean holdsLock(Object x) Возвращает истину, если текущий поток удерживает блокировку данного объекта. |
4 | public static Thread currentThread() Возвращает ссылку на текущий выполняющийся поток, который вызывает этот метод. |
5 | 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 вам понадобятся следующие очень удобные концепции:
Что такое синхронизация потоков?
Обработка межпотоковой связи
Обработка тупика потока
Основные операции с потоками