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


Всяко приложение може да има множество процеси (инстанции). Всеки от тези процеси може да бъде присвоен или като единична нишка, или като множество нишки. В този урок ще видим как да изпълняваме множество задачи едновременно и ще научим повече за нишките и синхронизирането между нишките.

Какво е единична нишка?

Една нишка в Java е основно лека и най-малката единица за обработка. Java използва нишки чрез използване на „Клас нишка“. Има два вида конец – потребителска нишка и демон нишка (daemon threads се използват, когато искаме да почистим приложението и се използват във фонов режим). Когато дадено приложение стартира за първи път, се създава потребителска нишка. Публикувайки това, можем да създадем много потребителски нишки и демон нишки.

Пример за една нишка:

package demotest;

public class GuruThread
{
       public static void main(String[] args) {
              System.out.println("Single Thread");
       }
}

Предимства на единична нишка:

  • Намалява режийните разходи в приложението, тъй като се изпълнява една нишка в системата
  • Освен това намалява разходите за поддръжка на приложението.

В какво е Multithreading Java?

Многопоточност in Java е процес на изпълнение на две или повече нишки едновременно за максимално използване на процесора. Многонишковите приложения изпълняват две или повече нишки, работещи едновременно. Следователно, той е известен също като паралелност в Java. Всяка нишка върви успоредно една на друга. Множеството нишки не разпределят отделна област на паметта, следователно спестяват памет. Освен това превключването на контекста между нишките отнема по-малко време.

Пример за много нишка:

package demotest;
public class GuruThread1 implements Runnable
{
       public static void main(String[] args) {
        Thread guruThread1 = new Thread("Guru1");
        Thread guruThread2 = new Thread("Guru2");
        guruThread1.start();
        guruThread2.start();
        System.out.println("Thread names are following:");
        System.out.println(guruThread1.getName());
        System.out.println(guruThread2.getName());
    }
    @Override
    public void run() {
    }
}

Предимства на мултинишката:

  • Потребителите не са блокирани, тъй като нишките са независими и можем да извършваме множество операции на моменти
  • Като такива нишките са независими, другите нишки няма да бъдат засегнати, ако една нишка срещне изключение.

Жизнен цикъл на нишка в Java

Жизненият цикъл на една нишка:

Жизнен цикъл на нишка в Java
Жизнен цикъл на нишка в Java

Има различни етапи от жизнения цикъл на нишката, както е показано на горната диаграма:

  1. НОВ
  2. Изпълним
  3. Работещи
  4. Очакване
  5. Мъртъв
  1. Ново: В тази фаза нишката се създава с помощта на клас „Клас на нишка“.Той остава в това състояние до програмата започва нишката. Известен е още като роден конец.
  2. Работим: В тази страница екземплярът на нишката се извиква с метод за стартиране. Контролът на нишката се дава на планировчика, за да завърши изпълнението. Зависи от планировчика дали да стартира нишката.
  3. Работещи: Когато нишката започне да се изпълнява, тогава състоянието се променя на състояние „работи“. Планировчикът избира една нишка от пула от нишки и тя започва да се изпълнява в приложението.
  4. Очакване: Това е състоянието, когато нишката трябва да изчака. Тъй като в приложението се изпълняват множество нишки, има нужда от синхронизация между нишките. Следователно една нишка трябва да изчака, докато другата нишка бъде изпълнена. Следователно това състояние се нарича състояние на изчакване.
  5. Мъртви: Това е състоянието, когато нишката е прекратена. Нишката е в състояние на изпълнение и веднага щом завърши обработката, тя е в „мъртво състояние“.


Методи на многопоточност в Java

Някои от често използваните методи за нишки са:

Начин на доставка Descriptйон
начало() Този метод стартира изпълнението на нишката и JVM извиква метода run() на нишката.
Сън (int милисекунди) Този метод кара нишката да заспива, следователно изпълнението на нишката ще спре за предоставени милисекунди и след това нишката отново започва да се изпълнява. Това помага при синхронизирането на нишките.
getName () Връща името на нишката.
setPriority(int newpriority) Променя приоритета на нишката.
добив () Това кара текущата нишка да спре и други нишки да се изпълнят.

Пример: В тази многопоточна програма в Java Например, ще създадем нишка и ще изследваме вградените методи, налични за нишки.

package demotest;
public class thread_example1 implements Runnable {
    @Override
    public void run() {
    }
    public static void main(String[] args) {
        Thread guruthread1 = new Thread();
        guruthread1.start();
        try {
            guruthread1.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        guruthread1.setPriority(1);
        int gurupriority = guruthread1.getPriority();
        System.out.println(gurupriority);
        System.out.println("Thread Running");
  }
}

Обяснение на кода:

  • Кодов ред 2: Създаваме клас „thread_Example1“, който имплементира интерфейса Runnable (той трябва да бъде имплементиран от всеки клас, чиито екземпляри са предназначени да бъдат изпълнени от нишката.)
  • Кодов ред 4: Той отменя метода на изпълнение на интерфейса, който може да се изпълнява, тъй като е задължително да се отменя този метод
  • Кодов ред 6: Тук сме дефинирали основния метод, в който ще започнем изпълнението на нишката.
  • Кодов ред 7: Тук създаваме ново име на нишка като „guruthread1“ чрез инстанциране на нов клас нишка.
  • Кодов ред 8: ще използваме метода „старт“ на нишката, използвайки екземпляр „guruthread1“. Тук нишката ще започне да се изпълнява.
  • Кодов ред 10: Тук използваме метода „заспиване“ на нишката, използвайки екземпляр „guruthread1“. Следователно нишката ще заспи за 1000 милисекунди.
  • Код 9-14: Тук сме поставили метода на заспиване в блока try catch, тъй като има проверено изключение, което възниква, т.е. прекъснато изключение.
  • Кодов ред 15: Тук задаваме приоритета на нишката на 1, независимо от приоритета, който е бил
  • Кодов ред 16: Тук получаваме приоритета на нишката с помощта на getPriority()
  • Кодов ред 17: Тук отпечатваме стойността, извлечена от getPriority
  • Кодов ред 18: Тук пишем текст, който нишката работи.

Когато изпълните горния код, получавате следния изход:

Пример за нишка в Java

Изход:

5 е приоритетът на Thread, а Thread Running е текстът, който е изходът на нашия код.

Java Нишка Syncхронизация

При многонишковостта има асинхронно поведение на програмите. Ако една нишка записва някои данни и друга нишка, която чете данни едновременно, може да създаде несъответствие в приложението. Когато има нужда от достъп до споделените ресурси от две или повече нишки, тогава се използва подходът за синхронизация. Java предостави синхронизирани методи за прилагане на синхронизирано поведение.

При този подход, след като нишката достигне вътре в синхронизирания блок, тогава никоя друга нишка не може да извика този метод на същия обект. Всички нишки трябва да изчакат, докато тази нишка завърши синхронизирания блок и излезе от него. По този начин синхронизацията помага в многопоточно приложение. Една нишка трябва да изчака, докато другата нишка завърши своето изпълнение, само тогава другите нишки са разрешени за изпълнение.

Може да се запише в следната форма:

Synchronized(object)
{  
        //Block of statements to be synchronized
}

Многопоточност в Java Примерни програми

В тази многопоточност Java Например ще вземем две нишки и ще извлечем имената на нишката.

Пример1:

GuruThread1.java
package demotest;
public class GuruThread1 implements Runnable{

    /**
     * @param args
     */
    public static void main(String[] args) {
        Thread guruThread1 = new Thread("Guru1");
        Thread guruThread2 = new Thread("Guru2");
        guruThread1.start();
        guruThread2.start();
        System.out.println("Thread names are following:");
        System.out.println(guruThread1.getName());
        System.out.println(guruThread2.getName());
    }
    @Override
    public void run() {
    }
}

Обяснение на кода:

  • Кодов ред 3: Взехме клас „GuruThread1“, който имплементира Runnable (той трябва да бъде имплементиран от всеки клас, чиито екземпляри са предназначени да бъдат изпълнени от нишката.)
  • Кодов ред 8: Това е основният метод на класа
  • Кодов ред 9: Тук създаваме класа Thread и създаваме екземпляр с име „guruThread1“ и създаваме нишка.
  • Кодов ред 10: Тук създаваме клас Thread и създаваме екземпляр с име „guruThread2“ и създаваме нишка.
  • Кодов ред 11: Започваме нишката, т.е. guruThread1.
  • Кодов ред 12: Започваме нишката, т.е. guruThread2.
  • Кодов ред 13: Извеждане на текста като „Имената на нишките са следните:“
  • Кодов ред 14: Получаване на името на нишка 1 с помощта на метода getName() на класа на нишката.
  • Кодов ред 15: Получаване на името на нишка 2 с помощта на метода getName() на класа на нишката.

Когато изпълните горния код, получавате следния изход:

Java Пример за многопоточност

Изход:

Имената на нишките се извеждат тук като

  • Guru1
  • Guru2

Пример 2:

В тази многопоточност в Java Например, ще научим за заместването на методите run() и start() метода на изпълняващ се интерфейс и ще създадем две нишки от този клас и ще ги стартираме съответно.

Освен това взимаме два класа,

  • Такъв, който ще реализира работещия интерфейс и
  • Друг, който ще има основния метод и ще се изпълнява съответно.
package demotest;
public class GuruThread2 {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  GuruThread3 threadguru1 = new GuruThread3("guru1");
  threadguru1.start();
  GuruThread3 threadguru2 = new GuruThread3("guru2");
  threadguru2.start();
 }
}
class GuruThread3 implements Runnable {
 Thread guruthread;
 private String guruname;
 GuruThread3(String name) {
  guruname = name;
 }
 @Override
 public void run() {
  System.out.println("Thread running" + guruname);
  for (int i = 0; i < 4; i++) {
   System.out.println(i);
   System.out.println(guruname);
   try {
    Thread.sleep(1000);
   } catch (InterruptedException e) {
    System.out.println("Thread has been interrupted");
   }
  }
 }
 public void start() {
  System.out.println("Thread started");
  if (guruthread == null) {
   guruthread = new Thread(this, guruname);
   guruthread.start();
  }
 }
}

Обяснение на кода:

  • Кодов ред 2: Тук вземаме клас „GuruThread2“, който ще има главния метод в него.
  • Кодов ред 4: Тук вземаме основен метод на класа.
  • Кодов ред 6-7: Тук създаваме екземпляр на клас GuruThread3 (който е създаден в долните редове на кода) като „threadguru1“ и стартираме нишката.
  • Кодов ред 8-9: Тук създаваме друг екземпляр на клас GuruThread3 (който е създаден в долните редове на кода) като „threadguru2“ и стартираме нишката.
  • Кодов ред 11: Тук създаваме клас „GuruThread3“, който имплементира работещия интерфейс (трябва да бъде имплементиран от всеки клас, чиито екземпляри са предназначени да бъдат изпълнени от нишката.)
  • Кодов ред 13-14: ние вземаме две променливи на класа, от които едната е от класа тип нишка, а другата от класа низ.
  • Кодов ред 15-18: ние заместваме конструктора GuruThread3, който приема един аргумент като тип низ (който е име на нишки), който се присвоява на променливата на класа guruname и следователно името на нишката се съхранява.
  • Кодов ред 20: Тук заместваме метода run() на интерфейса, който може да се изпълнява.
  • Кодов ред 21: Ние извеждаме името на нишката с помощта на оператор println.
  • Кодов ред 22-31: Тук използваме for цикъл с брояч, инициализиран на 0, и той не трябва да бъде по-малък от 4 (можем да вземем произволно число, следователно тук цикълът ще се изпълнява 4 пъти) и увеличаваме брояча. Ние отпечатваме името на нишката и също така правим нишката да заспи за 1000 милисекунди в рамките на блок try-catch, тъй като методът на заспиване повдигна проверено изключение.
  • Кодов ред 33: Тук заместваме метода за стартиране на интерфейса, който може да се изпълнява.
  • Кодов ред 35: Извеждаме текста „Нишката е започната“.
  • Кодов ред 36-40: Тук вземаме условие if, за да проверим дали променливата на класа guruthread има стойност в нея или не. Ако е null, тогава създаваме екземпляр, използвайки клас нишка, който приема името като параметър (стойността, за която е зададена в конструктора). След което нишката се стартира с помощта на метода start().

Когато изпълните горния код, получавате следния изход:

Пример за многопоточност в Java

Продукция:

Следователно има две нишки, получаваме два пъти съобщение „Нишката е стартирана“.

Получаваме имената на нишката, както сме ги извели.

Влиза в for цикъл, където отпечатваме брояча и името на нишката и броячът започва с 0.

Цикълът се изпълнява три пъти и между тях нишката е заспала за 1000 милисекунди.

Следователно, първо получаваме guru1, след това guru2 и отново guru2, защото нишката спи тук за 1000 милисекунди и след това следва guru1 и отново guru1, нишката спи за 1000 милисекунди, така че получаваме guru2 и след това guru1.

Oбобщение

В този урок видяхме многонишкови приложения Java и как да използвате единична и многонишкова нишка в Java.

  • Обяснете многопоточността в Java: при многопоточност потребителите не се блокират, тъй като нишките са независими и могат да извършват множество операции наведнъж
  • Различни етапи от жизнения цикъл на нишката са,
    • НОВ
    • Изпълним
    • Работещи
    • Очакване
    • Мъртъв
  • Научихме и за синхронизация между нишки, което помага на приложението да работи гладко.
  • Многопоточно програмиране в Java улеснява много повече задачи на приложението.

Обобщете тази публикация с: