Проблеми, решени чрез многопоточност
Многонишковостта всъщност е изобретена за постигане на две важни цели:-
Правете няколко неща едновременно.
В примера по-горе различни нишки (членове на семейството) извършват няколко действия паралелно: измиват чинии, отиват до магазина и опаковат нещата.
Можем да предложим пример, по-тясно свързан с програмирането. Да предположим, че имате програма с потребителски интерфейс. Когато щракнете върху „Продължи“ в програмата, трябва да се извършат някои изчисления и потребителят трябва да види следния екран. Ако тези действия бяха извършени последователно, тогава програмата просто щеше да увисне, след като потребителят щракне върху бутона „Продължи“. Потребителят ще вижда екрана с екрана с бутона „Продължи“, докато програмата извърши всички вътрешни изчисления и достигне частта, в която потребителският интерфейс се обновява.
Е, предполагам, че ще изчакаме няколко minutesи!
Или можем да преработим нашата програма or, Howто казват програмистите, да я „паралелизираме“. Нека извършим нашите изчисления в една нишка и да начертаем потребителския интерфейс в друга. Повечето компютри имат достатъчно ресурси за това. Ако изберем този маршрут, тогава програмата няма да замръзне и потребителят ще се движи плавно между екраните, без да се притеснява Howво се случва вътре. Едното не пречи на другото :)
-
Извършвайте изчисления по-бързо.
Тук всичко е много по-просто. Ако нашият процесор има множество ядра, а повечето процесори днес имат, тогава няколко ядра могат да се справят с нашия списък от задачи паралелно. Очевидно, ако трябва да изпълним 1000 задачи и всяка от тях отнема една секунда, едно ядро може да завърши списъка за 1000 секунди, две ядра за 500 секунди, три за малко повече от 333 секунди и т.н.
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("I'm Thread! My name is " + getName());
}
}
За да създадем и стартираме нишки, трябва да създадем клас, да го накараме да наследи java.lang . Клас нишка и замени неговия метод run() . Последното изискване е много важно. Именно в метода run() ние дефинираме логиката за изпълнение на нашата нишка. Сега, ако създадем и стартираме екземпляр на MyFirstThread , методът run() ще покаже ред с име: методът getName() показва „системното“ име на нишката, което се присвоява автоматично. Но защо говорим условно? Нека създадем такъв и разберем!
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
Конзолен изход: Аз съм Thread! Казвам се Thread-2 Аз съм Thread! Казвам се Thread-1 Аз съм Thread! Казвам се Thread-0 Аз съм Thread! Казвам се Thread-3 Аз съм Thread! Казвам се Thread-6 Аз съм Thread! Казвам се Thread-7 Аз съм Thread! Казвам се Thread-4 Аз съм Thread! Казвам се Thread-5 Аз съм Thread! Казвам се Thread-9 Аз съм Thread! Казвам се Thread-8. Нека създадем 10 нишки ( обекти MyFirstThread , които наследяват Thread ) и да ги стартираме, като извикаме метода start() на всеки обект. След извикване на метода start() се изпълнява логиката в метода run() . Забележка: имената на нишките не са подредени. Странно е, че не бяха последователни:, Thread-1 , Thread-2 и така нататък? Както се случва, това е пример за време, когато „последователното“ мислене не пасва. Проблемът е, че сме предоставor само команди за създаване и изпълнение на 10 нишки. Планировчикът на нишки, специален механизъм на операционната система, определя техния ред на изпълнение. Неговият прецизен дизайн и стратегия за вземане на решения са теми за задълбочена дискусия, в които няма да се впускаме сега. Основното нещо, което трябва да запомните е, че програмистът не може да контролира реда на изпълнение на нишките. За да разберете сериозността на ситуацията, опитайте да изпълните метода main() в горния пример още няколко пъти. Изход от конзолата при второ изпълнение: Аз съм Thread! Казвам се Thread-0 Аз съм Thread! Казвам се Thread-4 Аз съм Thread! Казвам се Thread-3 Аз съм Thread! Казвам се Thread-2 Аз съм Thread! Казвам се Thread-1 Аз съм Thread! Казвам се Thread-5 Аз съм Thread! Казвам се Thread-6 Аз съм Thread! Казвам се Thread-8 Аз съм Thread! Казвам се Thread-9 Аз съм Thread! Казвам се Thread-7 Конзолен изход от третото изпълнение: Аз съм Thread! Казвам се Thread-0 Аз съм Thread! Казвам се Thread-3 Аз съм Thread! Казвам се Thread-1 Аз съм Thread! Казвам се Thread-2 Аз съм Thread! Казвам се Thread-6 Аз съм Thread! Казвам се Thread-4 Аз съм Thread! Казвам се Thread-9 Аз съм Thread! Казвам се Thread-5 Аз съм Thread! Казвам се Thread-7 Аз съм Thread! Казвам се Thread-8
Проблеми, създадени от многопоточност
В нашия пример с книгите видяхте, че многопоточността решава много важни задачи и може да направи нашите програми по-бързи. Често пъти по-бързо. Но многопоточността се счита за трудна тема. Наистина, ако се използва неправилно, той създава проблеми, instead of да ги решава. Когато казвам „създава проблеми“, нямам предвид в няHowъв абстрактен смисъл. Има два специфични проблема, които многонишковостта може да създаде: безизходица и състезателни условия. Безизходица е ситуация, при която множество нишки чакат ресурси, държани една от друга, и нито една от тях не може да продължи да работи. Ще говорим повече за това в следващите уроци. Следният пример ще е достатъчен за сега:
- Thread-1 спира да взаимодейства с Object-1 и превключва към Object-2 веднага щом Thread-2 спре да взаимодейства с Object-2 и превключва към Object-1.
- Thread-2 спира да взаимодейства с Object-2 и превключва към Object-1 веднага щом Thread-1 спре да взаимодейства с Object-1 и превключва към Object-2.
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("Thread executed: " + getName());
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
Сега си представете, че програмата отговаря за работата на робот, който готви храна! Thread-0 вади яйца от хладилника. Конец-1 включва котлона. Нишка-2 взема тиган и го слага на котлона. Нишка-3 пали печката. Конец-4 налива олио в тигана. Конец-5 счупваме яйцата и ги изсипваме в тигана. Нишка-6 хвърля черупките на яйцата в кофата за боклук. Нишка-7 премахва сварените яйца от горелката. Конец-8 слага сварените яйца в чиния. Нишка-9 мие чиниите. Вижте резултатите от нашата програма: Изпълнена нишка: Нишка-0 Изпълнена нишка: Нишка-2 Изпълнена нишка Нишка-1 Изпълнена нишка: Нишка-4 Изпълнена нишка: Нишка-9 Изпълнена нишка: Нишка-5 Изпълнена нишка: Нишка-8 Нишка изпълнена: Нишка-7 Изпълнена нишка: Нишка-3 Това рутинна комедия ли е? :) И всичко това, защото работата на нашата програма зависи от реда на изпълнение на нишките. При най-малкото нарушение на необходимата последователност, нашата кухня се превръща в ад, а луд робот унищожава всичко около нея. Това също е често срещан проблем при многопоточното програмиране. Ще чуете за това повече от веднъж. В заключение на този урок бих искал да препоръчам книга за многопоточността. 
GO TO FULL VERSION