многопочность

Многопоточность в Java

Продолжаем изучать стандартную библиотеку языка программирования Java. И сегодня речь пойдет о многопоточности:

  • что такое многопоточность;
  • как ее реализовать;
  • как создать о остановить потоки выполнения.

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

Многопоточность — это свойство системы выполнять несколько вычислений одновременно, тем самым ускоряя процесс этого вычисления. Например, когда Вы играете в компьютерные игры, вы видите, что Ваш персонаж выполняет определенное действие, другие персонажи, анимация, звук. Это все отдельные потоки если говорить примитивно.

В языке Java есть стандартный класс, который реализует многопоточность: Thread, который имплементирует Runable интерфейс. Для того, чтобы реализовать многопоточность в своей программе нужно унаследовать свой класс от Thread или имплементировать интерфейс Runable. Нечто похожее мы делали, когда создавали свои классы исключения в статье о исключениях. Но это еще не все. В классе Thread есть метод run() и start(), которые созданы чтобы делать вычисления и запускать выполнение кода соответственно. То есть в методе run() мы пишем, что хотим выполнить, а когда вызываем метод start(), он автоматически запускает наш код в run. Вот такая многоходовочка)). Все гораздо проще, когда смотришь на код.

Код    
  1. package com.java;
  2.  
  3. public class MyMultithreadClass implements Runnable {//создаем наш многопоточный класс имплементируя его от Runnable
  4.     public int i = 0;
  5.    
  6.     @Override
  7.     public void run() {//делаем реализацию метода run
  8.         // TODO Auto-generated method stub
  9.         System.out.println("My thread " + Thread.currentThread().getId() + " is getting " + i++);//попробуем посмотреть изменения переменной i
  10.     }
  11.  
  12.     public static void main(String[] args) {
  13.         MyMultithreadClass myMultithread = new MyMultithreadClass(); //создаем екземпляр нашего класса
  14.         new Thread(myMultithread).start();//создаем первый поток и одновременно запускаем его
  15.         new Thread(myMultithread).start();//второй
  16.         new Thread(myMultithread).start();//третий !!! порядок потоков в методе вовсе не означает, что они выполняться в таком порядке
  17.  
  18.     }
  19.  
  20. }

 

Результат работы программы может быть разным при каждом запуске:

Код    
  1. My thread 10 is getting 0
  2. My thread 12 is getting 2
  3. My thread 11 is getting 1

Как я уже прокомментировал в коде: результат будет не всегда совпадать с порядком следования вызовов в коде. Это зависит от множества факторов. Можно выставить приоритетность потоков. Тогда у каждого потока будет свой приоритет и результат будет более предсказуем, но из опыта скажу, что даже это не гарантирует строгий порядок выполнения потоков по приоритетам:

Код    
  1. package com.java;
  2.  
  3. public class MyMultithreadClass implements Runnable {//создаем наш многопоточный класс имплементируя его от Runnable
  4.     public int i = 0;
  5.    
  6.     @Override
  7.     public void run() {//делаем реализацию метода run
  8.         // TODO Auto-generated method stub
  9.         System.out.println("My thread " + Thread.currentThread().getId() + " is getting " + i++);//попробуем посмотреть изменения переменной i
  10.     }
  11.  
  12.     public static void main(String[] args) {
  13.         MyMultithreadClass myMultithread = new MyMultithreadClass(); //создаем екземпляр нашего класса
  14.         Thread thread1 = new Thread(myMultithread);
  15.         Thread thread2 = new Thread(myMultithread);
  16.         Thread thread3 = new Thread(myMultithread);
  17.         thread1.setPriority(1);//можно задавать приоритет от 0 до 10
  18.         thread2.setPriority(9);//теперь результат будет более предсказуем
  19.         thread3.setPriority(5);
  20.         thread1.start();
  21.         thread2.start();
  22.         thread3.start();
  23.  
  24.     }
  25.  
  26. }

Теперь предлагаю посмотреть на второй метод создания многопоточности: унаследование от класса. Полагаю, Вы помните из статьи ООП, что унаследоваться в Java можно только от одного класса. В этом и недостаток такого метода. Ваш класс уже не сможет унаследовать другие классы:

Код    
  1. package com.java;
  2.  
  3. public class MyMultithreadClass extends Thread {//создаем наш многопоточный класс унаследуя его от Thread
  4.     public static int i = 0;//изменили переменную на static, чтобы она не была привязана к классу.
  5.    
  6.     @Override
  7.     public void run() {//делаем реализацию метода run
  8.         // TODO Auto-generated method stub
  9.         System.out.println("My thread " + Thread.currentThread().getId() + " is getting " + i++);//попробуем посмотреть изменения переменной i
  10.     }
  11.  
  12.    
  13.     public static void main(String[] args) {
  14.         MyMultithreadClass thread1 = new MyMultithreadClass();
  15.         MyMultithreadClass thread2 = new MyMultithreadClass();
  16.         MyMultithreadClass thread3 = new MyMultithreadClass();
  17.         thread1.start();
  18.         thread2.start();
  19.         thread3.start();
  20.     }
  21.  
  22. }

Результат выполнения все еще не предсказуем: 

Код    
  1. My thread 10 is getting 0
  2. My thread 12 is getting 2
  3. My thread 11 is getting 1

Как видите, создавать потоки совсем не сложно. Несколько сложнее — это управлять ими и ресурсами, которые они используют. Нужно быть очень внимательным при работе с потоками, так как результаты могут быть не такими, которых вы ожидаете.

Потоки имеют определенные состояния. Всего их 4:

  • создание (когда мы написали new Thread();
  • старт (thread1.start());
  • выполнение (пока выполняется метод run());
  • завершение (когда поток выполнил свою работу).

Вот Вам полезная картинка:

многопоточность в Java

Как видим на рисунке, поток может еще и ожидать. Для того, чтобы на время заставить поток прекратить свою работу в классе есть метод wait(), который приостанавливает выполнение пока не будет вызван метод notify(). Неправильно говорить, что это методы класса потока. Это методы объекта. Может у Вас когда то будет вопрос на тесте или собеседовании назвать методы класса Object; тогда к тем методам что Вы вспомните можете смело называть wait() и notify().

Из статических методов есть метод sleep(), который принимает целочисленную переменную в качестве миллисекунд на который следует приостановить поток. Вызов этого метода можно осуществлять в любом месте кода, где Вы желаете приостановить или замедлить выполнение. Я часто использовал этот метод в циклах, когда нужно было в консоли увидеть большое количество данных и найти нужное. Да, поначалу, я дебажил в консоли)).

Код    
  1. package com.javamaster;
  2.  
  3. public class SleepExample {
  4.  
  5.     public static void main(String [] args){
  6.         for (int i = 0; i < 20; i++) {
  7.             System.out.println(i);
  8.             try {
  9.                 Thread.sleep(500);
  10.             } catch (InterruptedException e) {
  11.                 // TODO Auto-generated catch block
  12.                 e.printStackTrace();
  13.             }
  14.         }
  15.     }
  16. }

 

Таким образом переменная будет выводиться с задержкой в 500 миллисекунд. Не забывайте только, что данный метод может выбрасывать InterruptedException поэтому, при его вызове нужно или оборачивать его в блок try-catch или прописать throws InterruptedException после названия метода.

Есть еще метод yield(), при вызове которого, поток на время прекращает работу, позволяя другим потокам тоже выполниться.

Чтобы уничтожить потоки есть методы stop() и destroy(). Разница между ними в том, что при вызове destroy() поток уже нельзя возобновить. Можно прервать выполнение потока вызвав метод interrupt().

Есть еще много других методов, но для начала этого Вам будет достаточно. Многопоточность это очень объемная тема, которую сложно охватить одной статьей. Следите за обновлениями сайта и возможно еще будет туториал посвящен многопоточности, но уже с более практичными примерами и задачами.

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