Как запустить поток java
Перейти к содержимому

Как запустить поток java

  • автор:

Потоки. Класс Thread и интерфейс Runnable

В русской терминологии за термином Thread укрепился перевод «Поток». Хотя это слово также можно перевести как «Нить». Иногда в зарубежных учебных материалах понятие потока объясняется именно на нитях. Продолжим логический ряд — там где нити, там и клубок. А где клубок, там и кот. Сразу видно, что у переводчиков не было котов. Так и возникла путаница. Тем более что существуют другие потоки под термином Stream. Переводчики, вообще странный народ.

Когда запускается любое приложение, то начинает выполняться поток, называемый главным потоком (main). От него порождаются дочерние потоки. Главный поток, как правило, является последним потоком, завершающим выполнение программы.

Несмотря на то, что главный поток создаётся автоматически, им можно управлять через объект класса Thread. Для этого нужно вызвать метод currentThread(), после чего можно управлять потоком.

Класс Thread содержит несколько методов для управления потоками.

  • getName() — получить имя потока
  • getPriority() — получить приоритет потока
  • isAlive() — определить, выполняется ли поток
  • join() — ожидать завершение потока
  • run() — запуск потока. В нём пишите свой код
  • sleep() — приостановить поток на заданное время
  • start() — запустить поток

Получим информацию о главном потоке и поменяем его имя.

 Thread mainThread = Thread.currentThread(); mInfoTextView.setText("Текущий поток: " + mainThread.getName()); // Меняем имя и выводим в текстовом поле mainThread.setName("CatThread"); mInfoTextView.append("\nНовое имя потока: " + mainThread.getName()); 

Имя у главного потока по умолчанию main, которое мы заменили на CatThread.

Вызовем информацию о названии потока без указания метода.

 Thread mainThread = Thread.currentThread(); mInfoTextView.setText("Текущий поток: " + mainThread); 

В этом случае можно увидеть строчку Thread[main,5,main] — имя потока, его приоритет и имя его группы.

Создание собственного потока

Создать собственный поток не сложно. Достаточно наследоваться от класса Thread.

Объявим внутри нашего класса внутренний класс и вызовем его по щелчку, вызвав метод start().

 public class MyThread extends Thread < public void run() < Log.d(TAG, "Mой поток запущен. "); >> public void onClick(View view)

Как вариант, перенести вызов метода start() в конструктор.

 public void onClick(View view) < MyThread myThread = new MyThread(); >public class MyThread extends Thread < // Конструктор MyThread() < // Создаём новый поток super("Второй поток"); Log.i(TAG, "Создан второй поток " + this); start(); // Запускаем поток >public void run() < Log.d(TAG, "Mой поток запущен. "); try < for (int i = 5; i >0; i--) < Log.i(TAG, "Второй поток: " + i); Thread.sleep(500); >> catch (InterruptedException e) < Log.i(TAG, "Второй поток прерван"); >> > 

Создание потока с интерфейсом Runnable

Есть более сложный вариант создания потока. Для создания нового потока нужно реализовать интерфейс Runnable. Вы можете создать поток из любого объекта, реализующего интерфейс Runnable и объявить метод run().

Внутри метода run() вы размещаете код для нового потока. Этот поток завершится, когда метод вернёт управление.

Когда вы объявите новый класс с интерфейсом Runnable, вам нужно использовать конструктор:

 Thread(Runnable объект_потока, String имя_потока) 

В первом параметре указывается экземпляр класса, реализующего интерфейс. Он определяет, где начнётся выполнение потока. Во втором параметре передаётся имя потока.

После создания нового потока, его нужно запустить с помощью метода start(), который, по сути, выполняет вызов метода run().

Создадим новый поток внутри учебного проекта в виде вложенного класса и запустим его.

 package ru.alexanderklimov.expresscourse; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import static ru.alexanderklimov.expresscourse.R.id.textViewInfo; public class MainActivity extends AppCompatActivity < final String TAG = "ExpressCourse"; private Button mButton; private EditText mResultEditText; private TextView mInfoTextView; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton = findViewById(R.id.buttonGetResult); mResultEditText = findViewById(R.id.editText); mInfoTextView = findViewById(textViewInfo); >public void onClick(View view) < new MyRunnable(); // создаём новый поток try < for (int i = 5; i >0; i--) < Log.i(TAG, "Главный поток: " + i); Thread.sleep(1000); >> catch (InterruptedException e) < Log.i(TAG, "Главный поток прерван"); >> class MyRunnable implements Runnable < Thread thread; // Конструктор MyRunnable() < // Создаём новый второй поток thread = new Thread(this, "Поток для примера"); Log.i(TAG, "Создан второй поток " + thread); thread.start(); // Запускаем поток >// Обязательный метод для интерфейса Runnable public void run() < try < for (int i = 5; i >0; i--) < Log.i(TAG, "Второй поток: " + i); Thread.sleep(500); >> catch (InterruptedException e) < Log.i(TAG, "Второй поток прерван"); >> > > 

Внутри конструктора MyRunnable() мы создаём новый объект класса Thread

thread = new Thread(this, "Поток для примера");

В первом параметре использовался объект this, что означает желание вызвать метод run() этого объекта. Далее вызывается метод start(), в результате чего запускается выполнение потока, начиная с метода run(). В свою очередь метод запускает цикл для нашего потока. После вызова метода start(), конструктор MyRunnable() возвращает управление приложению. Когда главный поток продолжает свою работу, он входит в свой цикл. После этого оба потока выполняются параллельно.

Можно запускать несколько потоков, а не только второй поток в дополнение к первому. Это может привести к проблемам, когда два потока пытаюсь работать с одной переменной одновременно.

Ключевое слово syncronized — синхронизированные методы

Для решения проблемы с потоками, которые могут внести путаницу, используется синхронизация.

Метод может иметь модификатор syncronized. Когда поток находится внутри синхронизированного метода, все другие потоки, которые пытаются вызвать его в том же экземпляре, должны ожидать. Это позволяет исключить путаницу, когда несколько потоков пытаются вызвать метод.

 syncronized void meow(String msg); 

Кроме того, ключевое слово syncronized можно использовать в качестве оператора. Вы можете заключить в блок syncronized вызовы методов какого-нибудь класса:

 syncronized(объект) < // операторы, требующие синхронизации >

Looper

Поток имеет в своём составе сущности Looper, Handler, MessageQueue.

Каждый поток имеет один уникальный Looper и может иметь много Handler.

Считайте Looper вспомогательным объектом потока, который управляет им. Он обрабатывает входящие сообщения, а также даёт указание потоку завершиться в нужный момент.

Поток получает свой Looper и MessageQueue через метод Looper.prepare() после запуска. Looper.prepare() идентифицирует вызывающий потк, создаёт Looper и MessageQueue и связывает поток с ними в хранилище ThreadLocal. Метод Looper.loop() следует вызывать для запуска Looper. Завершить его работу можно через метод looper.quit().

 class LooperThread extends Thread < public Handler mHandler; public void run() < Looper.prepare(); mHandler = new Handler() < public void handleMessage(Message msg) < // process incoming messages here >>; Looper.loop(); > > 

Используйте статический метод getMainLooper() для доступа к Looper главного потока:

 Looper mainLooper = Looper.getMainLooper(); 

Создадим два потока. Один запустим в основном потоке, а второй отдельно от основного. Нам будет достаточно двух кнопок и метки.

Обратите внимание, как запускаются потоки. Первый поток запускается с помощью метода start(), а второй — run(). Затем проверяем, в каком потоке мы находимся.

 package ru.alexanderklimov.as23; import android.os.Bundle; import android.os.Looper; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity < TextView mInfoTextView; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button startButton = (Button) findViewById(R.id.button_start); Button runButton = (Button) findViewById(R.id.button_run); mInfoTextView = (TextView) findViewById(R.id.textview_info); startButton.setOnClickListener(new View.OnClickListener() < @Override public void onClick(View view) < Thread thread = new Thread(new MyRunnable()); thread.start(); //в фоновом потоке >>); runButton.setOnClickListener(new View.OnClickListener() < @Override public void onClick(View view) < Thread thread = new Thread(new MyRunnable()); thread.run(); //в текущем потоке >>); > private class MyRunnable implements Runnable < @Override public void run() < // Проверяем, в каком потоке находимся if (Looper.getMainLooper().getThread() == Thread.currentThread()) < mInfoTextView.setText("В основном потоке"); >else < runOnUiThread(new Runnable() < @Override public void run() < mInfoTextView.setText("В фоновом потоке"); >>); > > > > 

Эта тема достаточно сложная и для большинства не представляет интереса и необходимости изучать.

В Android потоки в чистом виде используются всё реже и реже, у системы есть собственные способы.

Как запустить поток java

Для создания нового потока мы можем создать новый класс, либо наследуя его от класса Thread, либо реализуя в классе интерфейс Runnable .

Наследование от класса Thread

Создадим свой класс на основе Thread:

class JThread extends Thread < JThread(String name)< super(name); >public void run() < System.out.printf("%s started. \n", Thread.currentThread().getName()); try< Thread.sleep(500); >catch(InterruptedException e) < System.out.println("Thread has been interrupted"); >System.out.printf("%s fiished. \n", Thread.currentThread().getName()); > > public class Program < public static void main(String[] args) < System.out.println("Main thread started. "); new JThread("JThread").start(); System.out.println("Main thread finished. "); >>

Класс потока называется JThread. Предполагается, что в конструктор класса передается имя потока, которое затем передается в конструктор базового класса. В конструктор своего класса потока мы можем передать различные данные, но главное, чтобы в нем вызывался конструктор базового класса Thread, в который передается имя потока.

И также в JThread переопределяется метод run() , код которого собственно и будет представлять весь тот код, который выполняется в потоке.

В методе main для запуска потока JThread у него вызывается метод start() , после чего начинается выполнение того кода, который определен в методе run:

new JThread("JThread").start();
Main thread started. Main thread finished. JThread started. JThread finished.

Здесь в методе main в конструктор JThread передается произвольное название потока, и затем вызывается метод start() . По сути этот метод как раз и вызывает переопределенный метод run() класса JThread.

Обратите внимание, что главный поток завершает работу раньше, чем порожденный им дочерний поток JThread.

Аналогично созданию одного потока мы можем запускать сразу несколько потоков:

public static void main(String[] args)

Main thread started. Main thread finished. JThread 2 started. JThread 5 started. JThread 4 started. JThread 1 started. JThread 3 started. JThread 1 finished. JThread 2 finished. JThread 5 finished. JThread 4 finished. JThread 3 finished.

Ожидание завершения потока

При запуске потоков в примерах выше Main thread завершался до дочернего потока. Как правило, более распространенной ситуацией является случай, когда Main thread завершается самым последним. Для этого надо применить метод join() . В этом случае текущий поток будет ожидать завершения потока, для которого вызван метод join:

public static void main(String[] args) < System.out.println("Main thread started. "); JThread t= new JThread("JThread "); t.start(); try< t.join(); >catch(InterruptedException e) < System.out.printf("%s has been interrupted", t.getName()); >System.out.println("Main thread finished. "); >

Метод join() заставляет вызвавший поток (в данном случае Main thread) ожидать завершения вызываемого потока, для которого и применяется метод join (в данном случае JThread).

Main thread started. JThread started. JThread finished. Main thread finished.

Если в программе используется несколько дочерних потоков, и надо, чтобы Main thread завершался после дочерних, то для каждого дочернего потока надо вызвать метод join.

Реализация интерфейса Runnable

Другой способ определения потока представляет реализация интерфейса Runnable . Этот интерфейс имеет один метод run :

interface Runnable

В методе run() собственно определяется весь тот код, который выполняется при запуске потока.

После определения объекта Runnable он передается в один из конструкторов класса Thread:

Thread(Runnable runnable, String threadName)

Для реализации интерфейса определим следующий класс MyThread:

class MyThread implements Runnable < public void run()< System.out.printf("%s started. \n", Thread.currentThread().getName()); try< Thread.sleep(500); >catch(InterruptedException e) < System.out.println("Thread has been interrupted"); >System.out.printf("%s finished. \n", Thread.currentThread().getName()); > > public class Program < public static void main(String[] args) < System.out.println("Main thread started. "); Thread myThread = new Thread(new MyThread(),"MyThread"); myThread.start(); System.out.println("Main thread finished. "); >>

Реализация интерфейса Runnable во многом аналогична переопределению класса Thread. Также в методе run определяется простейший код, который усыпляет поток на 500 миллисекунд.

В методе main вызывается конструктор Thread, в который передается объект MyThread. И чтобы запустить поток, вызывается метод start() . В итоге консоль выведет что-то наподобие следующего:

Main thread started. Main thread finished. MyThread started. MyThread finished.

Поскольку Runnable фактически представляет функциональный интерфейс, который определяет один метод, то объект этого интерфейса мы можем представить в виде лямбда-выражения:

public class Program < public static void main(String[] args) < System.out.println("Main thread started. "); Runnable r = ()-> < System.out.printf("%s started. \n", Thread.currentThread().getName()); try< Thread.sleep(500); >catch(InterruptedException e) < System.out.println("Thread has been interrupted"); >System.out.printf("%s finished. \n", Thread.currentThread().getName()); >; Thread myThread = new Thread(r,"MyThread"); myThread.start(); System.out.println("Main thread finished. "); > >

Java. Потоки выполнения. Методы класса Thread

Потоки выполнения. Методы класса Thread : getName() , run() , start() , sleep() . Примеры использования

Перед изучением данной темы рекомендуется ознакомиться со следующей темой:

Поиск на других ресурсах:

1. Метод getName() . Получить имя потока. Пример

Пример определения имени главного потока и дочернего потока. Дочерний поток реализован в классе MyThread , который содержит следующие элементы:

  • внутреннее поле thr , которе есть ссылкой на класс Thread ;
  • конструтор MyThread (String) . Конструктор получает входным параметром имя создаваемого потока, который запускается на выполнение;
  • метод run() . В этом методе запрограммирована некоторая работа, которую выполняет поток;
  • метод getNameThread() — возвращает имя созданного потока путем вызова метода getName() .
// Класс, инкапсулирующий поток выполнения class MyThread implements Runnable < Thread thr; // ссылка на текущий поток // Конструктор класса MyThread - получает название потока MyThread(String name) < // Создать экземпляр thr и установить ему название потока thr = new Thread(this, name); // Запустить поток на выполнение thr.start(); // вызывается метод run() > // Точка входа в поток - метод run()  public void run() < // выполнить некоторую работу в потоке System.out.println("MyThread.run() - begin"); for (int i=0; i try < Thread.sleep(5); >  catch (InterruptedException e) < System.out.println(e.getMessage()); > > System.out.println("MyThread.run() - end"); > // Метод, возвращающий имя потока thr String getNameThread() < // вызвать метод getName() класса Thread return thr.getName(); > > public class Threads <  public static void main(String[] args) < // Метод getName() - получить имя потока. // 1. Получить имя главного потока и вывести его на экран Thread mainThread = Thread.currentThread(); String name = mainThread.getName(); // name = "main" System.out.println("name of main Thread color: #008000;"> // 2. Получить имя дочернего потока // 2.1. Создать дочерний поток и задать ему имя MyThread t1 = new MyThread("Thread-1"); // 2.2. Вывести имя дочернего потока для контроля System.out.println(t1.getNameThread()); > >

Результат выполнения программы

name of main Thread = main Thread-1 MyThread.run() - begin MyThread.run() - end
2. Методы run() , start() . Запуск и выполнение потока. Пример

Методы start() и run() используются при запуске и выполнении потока. Метод start() используется для указания того, что поток выполнения нужно запустить. В своем коде метод start() обращается к методу run() .

Общая форма метода start() следующая:

public void start()

В методе run() указывается код, который должен выполняться в потоке. Метод run() является точкой входа в поток. Метод run() объявлен в интерфейсе Runnable . Для того, чтобы в классе инкапсулировать поток выполнения нужно реализовывать интерфейс Runnable и переопределять метод run() по образцу ниже

class MyThreadClass implements Runnable < // . void run() < // код потока // . > >

Код метода run() начинает выполняться только после вызова метода start() . Общая форма метода run() следующая:

void run()

Пример. В примере демонстрируются различные способы запуска и создания потока. В этих способах используются методы start() и run() класса Thread .

// Создание потока с использованием способа наследования класса Thread class MyThread1 extends Thread < // Метод, который начинает выполнение потока  public void startThread1() < // Метод вызывает метод start() класса Thread для запуска потока // 1. Создать поток, укзав ему собственный экземпляр Thread thr = new Thread(this, "MyThread1"); // 2. Запустить поток thr.start(); // вызывается метод run() > // Метод run() класса  public void run() < // Вывести сообщение о начале выполнения потока System.out.println("MyThread1.run() - begin"); try < Thread.sleep(500); // выполнить некоторую работу в потоке for (int i=0; i  catch (InterruptedException e) < e.getMessage(); > // Вывести сообщение об окончании выполнения потока System.out.println("MyThread1.run() - end"); > > // Создание потока с использованием способа реализации интерфейса Runnable class MyThread2 implements Runnable < // Метод, который начинает выполнение потока  public void startThread2() < // Метод вызывает метод start() класса Thread для запуска потока // 1. Создать поток, указав ему собственный экземпляр Thread thr = new Thread(this, "MyThread1"); // 2. Запустить поток thr.start(); // вызывается метод run() > // Метод run() - выполнение потока  public void run() < // Вывести сообщение о начале выполнения потока System.out.println("MyThread2.run() - begin"); try < Thread.sleep(500); // выполнить некоторую работу в потоке for (int i=0; i  catch (InterruptedException e) < e.getMessage(); > // Вывести сообщение об окончании выполнения потока System.out.println("MyThread2.run() - end"); > > public class Threads <  public static void main(String[] args) < // Демонстрация методов start() и run() // 1. Объявить экземпляры классов MyThread1 и MyThread2 MyThread1 mt1 = new MyThread1(); MyThread2 mt2 = new MyThread2(); // 2. Запустить потоки на выполнение mt1.startThread1(); mt2.startThread2(); > >

Результат выполнения программы

MyThread1.run() - begin MyThread2.run() - begin MyThread1.run() - end MyThread2.run() - end
3. Метод sleep() . Задержка выполнения потока. Пример

Метод sleep() используется для указания задержки в миллисекундах в выполняемом потоке. После этого поток, который выполняется будет приостановлен на указанное время, что позволит выполнить другие (если таковые имеются) параллельно-выполняемые потоки.

В документации Java общая форма метода sleep() следующая

public static void sleep(long millis) throws InterruptedException
  • millis – количество миллисекунд, на которые приостанавливается поток.

Если нужно установить задержку в 1 секунду, то вызов метода sleep() в программном коде может быть, например, таким:

. try < Thread.sleep(1000); // Код выполнения потока // . > catch (InterruptedException e) < // . >

Пример. В примере создается задержка в 2 секунды для главного и дочернего потоков.

// Класс, инкапсулирующий поток class MyThread implements Runnable <  public void run() < // Начало выполнения потока System.out.println("MyThread.run() - begin"); // Установить задержку в 2 секунды  try < Thread.sleep(2000); >  catch (InterruptedException e) < e.getMessage(); > // Конец выполнения потока System.out.println("MyThread.run() - end"); > > public class Threads <  public static void main(String[] args) throws InterruptedException < // Вывести сообщение о том, что выполняется главный поток System.out.println("Main thread - begin"); // Создать экземпляр класса MyThread MyThread mt = new MyThread(); // Создать дочерний поток в главном потоке, // передать ему экземпляр mt Thread thr = new Thread(mt, "Thread - mt"); // Запустить поток методом start() thr.start(); // thr.run(); - так тоже можно запустить поток // Приостановить выполнение главного потока на 2 секунды Thread.sleep(2000); // Информировать о завершении главного потока System.out.println("Main thread - end"); > >

Связанные темы

  • Многозадачность. Потоки выполнения. Основные понятия
  • Средства Java для работы с потоками выполнения. Класс Thread . Интерфейс Runnable . Главный поток выполнения. Создание дочернего потока
  • Методы класса Thread : join() , isAlive() , getPriority() , setPriority() . Примеры
  • Примеры решения задач на потоки выполнения (Threads). Работа с файлами в потоках. Сортировка в потоках

Как запустить поток java

Чтобы запустить поток в Java , необходимо выполнить следующие шаги:

  • Создать класс, реализующий интерфейс Runnable . В этом классе должен быть определен метод run() , в котором будет содержаться код, который будет выполняться в отдельном потоке.
  • Создать экземпляр класса Thread , передав в конструктор объект класса, реализующего интерфейс Runnable
  • Вызвать метод start() у экземпляра класса Thread
public class MyRunnable implements Runnable  public void run()  // код, который будет выполняться в отдельном потоке > > public class Main  public static void main(String[] args)  MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); > > 

В этом примере создается объект MyRunnable , реализующий интерфейс Runnable , и передается в конструктор Thread .

Затем вызывается метод start() у Thread , который запускает выполнение кода из метода run() в отдельном потоке.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *