Поток — это базовая единица работы операционной системы. Он инкапсулирован в процесс. Процесс может содержать несколько потоков. Даже если мы не создадим поток вручную, в процессе будет запущен поток по умолчанию.
Что касается JVM, когда мы пишем для запуска однопоточную программу, в JVM выполняются как минимум два потока: один — это программа, которую мы создали, а другой — сбор мусора.
Основная информация о теме
Мы можем получить некоторую информацию о текущем потоке с помощью метода Thread.currentThread() и изменить ее.
Давайте посмотрим на следующий код:
Thread.currentThread().setName("Тест");
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
имя = Thread.currentThread().getName();
приоритет = Thread.currentThread().getPriority();
groupName = Thread.currentThread().getThreadGroup().getName();
isDaemon = Thread.currentThread().isDaemon();
System.out.println("Имя потока:" + имя);
System.out.println("Приоритет:" + приоритет);
GroupName , каждый поток будет входить в группу потоков по умолчанию. Мы также можем явно создать группу потоков. Группа потоков также может содержать группы подпотоков, чтобы потоки и группы потоков образовывали древовидную структуру.
Name , каждый поток будет иметь имя. Если не указано явно, правило имени — «Thread-xxx».
Priority , каждый поток будет иметь свой собственный приоритет, а способ обработки приоритета в JVM является «превентивным». Когда JVM находит поток с высоким приоритетом, она немедленно запускает этот поток для нескольких потоков с одинаковыми приоритетами, которые JVM опрашивает. Приоритет потока Java варьируется от 1 до 10, значение по умолчанию — 5. Класс Thread определяет две константы: MIN_PRIORITY и MAX_PRIORITY для представления наивысшего и наименьшего приоритетов.
Мы можем посмотреть на следующий код, который определяет два потока с разными приоритетами:
Поток thread2 = новый поток («высокий»)
{
публичный недействительный запуск()
{
для (int я = 0; я <5; я++)
{
System.out.println("Поток 2 запущен.");
}
}
};
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);
поток1.start();
поток2.start();
}
поток1.start();
}
Как создать тему
Все приведенное выше содержимое демонстрирует некоторую информацию в потоке по умолчанию, так как же создать поток? В Java есть три способа создания потоков.
Потоки в Java либо наследуют класс Thread, либо реализуют интерфейс Runnable. Давайте рассмотрим их один за другим.
Используйте внутренние классы для создания потоков
Мы можем использовать внутренние классы для создания потоков. Процесс заключается в объявлении переменной типа Thread и переопределении метода run. Пример кода выглядит следующим образом:
Мы можем получить класс от Thread и переопределить его метод запуска аналогично описанному выше. Пример кода выглядит следующим образом:
общественная статическая пустота createThreadBySubClass()
{
Поток MyThread = новый MyThread();
поток.start();
}
Мы можем определить класс для реализации интерфейса Runnable, а затем использовать экземпляр этого класса в качестве параметра для создания конструктора переменной Thread. Пример кода выглядит следующим образом:
общественная статическая пустота createThreadByRunnable()
{
MyRunnable runnable = новый MyRunnable();
Поток потока = новый поток (работоспособный);
поток.start();
}
Это включает в себя режим многопоточности в Java. Для Java, когда многопоточность работает, существуют различия между «многообъектной многопоточностью» и «однообъектной многопоточностью»:
Многообъектная многопоточность : во время работы программа создает несколько объектов потока, и для каждого объекта выполняется один поток.
Многопоточность одного объекта : программа создает объект потока во время работы и запускает на нем несколько потоков.
Очевидно, что с точки зрения синхронизации и планирования потоков многопоточность проще. Из трех вышеперечисленных методов создания потоков первые два являются «многообъектным многопоточным», а третий может использовать либо «многообъектный многопоточный», либо «однообъектный однопоточный».
Давайте рассмотрим следующий пример кода, который будет использовать метод Object.notify. Этот метод активирует поток объекта, а метод Object.notifyAll активирует все потоки объекта.
public static void main(String[] args) выдает InterruptedException
{
уведомитьТест();
уведомитьTest2();
уведомитьTest3();
}
Private static void notifyTest() выдает InterruptedException
{
MyThread[] arrThreads = новый MyThread[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = новый MyThread();
arrThreads[i].id = я;
arrThreads[i].setDaemon(истина);
arrThreads[i].start();
}
Thread.sleep(500);
for (int i = 0; i < arrThreads.length; i++)
{
синхронизировано (arrThreads [i])
{
arrThreads[i].notify();
}
}
}
Private static void notifyTest2() выдает InterruptedException
{
MyRunner[] arrMyRunners = новый MyRunner[3];
Thread[] arrThreads = новый поток[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrMyRunners[i] = новый MyRunner();
arrMyRunners[i].id = я;
arrThreads[i] = новая тема (arrMyRunners[i]);
arrThreads[i].setDaemon(истина);
arrThreads[i].start();
}
Thread.sleep(500);
for (int i = 0; i < arrMyRunners.length; i++)
{
синхронизировано (arrMyRunners [i])
{
arrMyRunners[i].notify();
}
}
}
Private static void notifyTest3() выдает InterruptedException
{
MyRunner бегун = новый MyRunner();
Thread[] arrThreads = новый поток[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = новый поток (бегун);
arrThreads[i].setDaemon(истина);
arrThreads[i].start();
}
Thread.sleep(500);
синхронизированный (бегун)
{
бегун.notifyAll();
}
}
}
класс MyThread расширяет поток
{
общественный ИНТ идентификатор = 0;
публичный недействительный запуск()
{
System.out.println("Поток " + id + " готов к спящему режиму на 5 минут.");
пытаться
{
синхронизировано(это)
{
this.wait(5*60*1000);
}
}
улов (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("Поток "th" + id + "разбужен.");
}
}
класс MyRunner реализует Runnable
{
общественный ИНТ идентификатор = 0;
публичный недействительный запуск()
{
System.out.println("Поток " + id + " готов к спящему режиму на 5 минут.");
пытаться
{
синхронизировано(это)
{
this.wait(5*60*1000);
}
}
улов (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("Поток "th" + id + "разбужен.");
}
}
Метод notifyAll подходит для сценария «один объект в многопоточном режиме», поскольку метод notify будет случайным образом активировать только один поток на объекте.
Переключение состояний потока
Для потока с момента его создания и до момента его завершения статус потока во время этого процесса может быть таким:
Создание: экземпляр потока уже существует, но у ЦП все еще есть выделенные ему ресурсы и временные интервалы.
Готов: поток получил все ресурсы, необходимые для работы, и просто ждет, пока процессор запланирует время.
Выполняется: поток находится в текущем интервале времени ЦП и выполняет соответствующую логику.
Sleep: обычно состояние после вызова Thread.sleep. В это время поток все еще содержит различные ресурсы, необходимые для работы, но не запланирован ЦП.
Приостановка: обычно состояние после вызова Thread.suspend. Подобно спящему режиму, процессор не планирует поток. Разница в том, что в этом состоянии поток освобождает все ресурсы.
Смерть: поток завершается или вызывается метод Thread.stop.
Далее мы продемонстрируем, как переключать состояния потока. Сначала мы воспользуемся следующим методом:
Thread() или Thread(Runnable): создать поток.
Thread.start: Запустить поток.
Thread.sleep: переключить поток в состояние сна.
Thread.interrupt: Прервать выполнение потока.
Thread.join: дождитесь завершения потока.
Thread.yield: Лишите поток времени выполнения на ЦП и дождитесь следующего планирования.
Object.wait: блокирует все потоки объекта и не будет продолжать работу до тех пор, пока не будет вызван метод уведомления.
Object.notify: случайное пробуждение потока на объекте.
Object.notifyAll: пробуждение всех потоков на объекте.
Теперь пришло время демонстрации! ! !
Тред ждет и просыпается
Здесь в основном используются методы Object.wait и Object.notify, см. пример уведомления выше. Следует отметить, что и wait, и notify должны быть нацелены на один и тот же объект. Когда мы создаем поток, реализуя интерфейс Runnable, мы должны использовать эти два метода для объекта Runnable вместо объекта Thread.
Тема сон и просыпание
public static void main(String[] args) выдает InterruptedException
{
Тест сна();
}
Private static void SleepTest() выдает InterruptedException
{
Поток потока = новый поток()
{
публичный недействительный запуск()
{
System.out.println("Thread" + Thread.currentThread().getName() + "Он будет спать 5 минут.");
пытаться
{
Thread.sleep(5*60*1000);
}
улов (InterruptedException ex)
{
System.out.println("Thread" + Thread.currentThread().getName() + "Сон был прерван.");
}
System.out.println("Thread" + Thread.currentThread().getName() + "Сон закончился.");
}
};
thread.setDaemon(истина);
поток.start();
Thread.sleep(500);
поток.прерывание();
}
}
Хотя существует метод Thread.stop, этот метод не рекомендуется. Мы можем использовать описанный выше механизм сна и пробуждения, чтобы позволить потоку завершить поток при обработке IterruptedException.
public static void main(String[] args) выдает InterruptedException
{
стопТест();
}
Private static void stopTest() выдает InterruptedException
{
Поток потока = новый поток()
{
публичный недействительный запуск()
{
System.out.println("Поток запущен.");
пытаться
{
Thread.sleep(1*60*1000);
}
улов (InterruptedException ex)
{
System.out.println("Прерывание потока, завершение потока");
возвращаться;
}
System.out.println("Поток завершился нормально.");
}
};
поток.start();
Thread.sleep(500);
поток.прерывание();
}
}
Когда мы создаем 10 подпотоков в основном потоке и ожидаем, что после завершения всех 10 подпотоков основной поток выполнит следующую логику. В это время наступает время появления Thread.join.
public static void main(String[] args) выдает InterruptedException
{
присоединитьсяТест();
}
Private static void joinTest() выдает InterruptedException
{
Поток потока = новый поток()
{
публичный недействительный запуск()
{
пытаться
{
for(int я = 0; я <5; я++)
{
System.out.println("Поток запущен.");
Thread.sleep(1000);
}
}
улов (InterruptedException ex)
{
ex.printStackTrace();
}
}
};
thread.setDaemon(истина);
поток.start();
Thread.sleep(1000);
поток.join();
System.out.println("Основной поток завершился нормально.");
}
}
Мы знаем, что все потоки процесса делят пространство памяти, так как же нам передавать сообщения между разными потоками? При рассмотрении ввода-вывода Java мы говорили о PipedStream и PipedReader, и именно здесь они вступают в игру.
Следующие два примера имеют одинаковые функции. Разница в том, что один использует Stream, а другой — Reader/Writer.
Поток thread1 = новый поток()
{
публичный недействительный запуск()
{
BufferedReader br = новый BufferedReader (новый InputStreamReader (System.in));
пытаться
{
пока (правда)
{
Строковое сообщение = br.readLine();
pos.write(message.getBytes());
if (message.equals("end")) сломать;
}
бр.закрыть();
поз.закрыть();
}
поймать (исключение ex)
{
ex.printStackTrace();
}
}
};
Поток thread2 = новый поток()
{
публичный недействительный запуск()
{
буфер байт[] = новый байт[1024];
int bytesRead = 0;
пытаться
{
while((bytesRead = pis.read(buffer, 0, buffer.length)) != -1)
{
System.out.println(новая строка(буфер));
if (new String(buffer).equals("end")) разрыв;
буфер = ноль;
буфер = новый байт[1024];
}
пис.закрыть();
буфер = ноль;
}
поймать (исключение ex)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(истина);
thread2.setDaemon(истина);
поток1.start();
поток2.start();
поток1.присоединиться();
поток2.присоединиться();
}
Поток thread1 = новый поток()
{
публичный недействительный запуск()
{
BufferedReader br = новый BufferedReader (новый InputStreamReader (System.in));
пытаться
{
пока (правда)
{
Строковое сообщение = br.readLine();
bw.write(сообщение);
bw.newLine();
чб.флеш();
if (message.equals("end")) сломать;
}
бр.закрыть();
пв.закрыть();
чб.закрыть();
}
поймать (исключение ex)
{
ex.printStackTrace();
}
}
};
Поток thread2 = новый поток()
{
публичный недействительный запуск()
{
Строковая линия = ноль;
пытаться
{
while((line = br.readLine()) != null)
{
System.out.println(строка);
if (line.equals("end")) разрыв;
}
бр.закрыть();
пр.закрыть();
}
поймать (исключение ex)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(истина);
thread2.setDaemon(истина);
поток1.start();
поток2.start();
поток1.присоединиться();
поток2.присоединиться();
}