CodeGym /Java блог /Случаен /Многопоточност в Java
John Squirrels
Ниво
San Francisco

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

Публикувано в групата
здрасти Първо, поздравления: стигнахте до темата за многопоточността в Java! Това е сериозно постижение — извървяхте дълъг път. Но се подгответе: това е една от най-трудните теми в курса. И не че използваме сложни класове or много методи тук: всъщност ще използваме по-малко от двадесет. По-скоро ще трябва леко да промените начина си на мислене. Преди вашите програми са бor изпълнявани последователно. Някои редове code идваха след други, някои методи идваха след други и всичко беше основно ясно. Първо изчислихме нещо, след това показахме резултата на конзолата и след това програмата приключи. За да разберете многопоточността, е по-добре да мислите от гледна точка на паралелизма. Нека започнем с нещо съвсем просто: ) Представете си, че вашето семейство се мести от една къща в друга. Събирането на всичките ви книги ще бъде важна част от преместването. Натрупали сте много книги и трябва да ги приберете в кашони. В момента вие сте единственият наличен. Мама приготвя храна, брат опакова дрехи, а сестра отиде до магазина. Сам можеш да се справиш няHow. Рано or късно ще изпълните задачата сами, но това ще отнеме много време. Сестра ви обаче ще се върне от магазина след 20 minutesи, а тя няма Howво друго да прави. Така че тя може да се присъедини към вас. Задачата не се е променила: поставете книгите в кутии. Но се изпълнява двойно по-бързо. Защо? Защото работата върви паралелно. Две различни „нишки“ (вие и сестра ви) изпълняват една и съща задача едновременно. И ако нищо не се промени, тогава ще има огромна времева разлика в сравнение със ситуацията, в която правите всичко сами. Ако брат си свърши работата скоро, може да ти помогне и нещата ще тръгнат още по-бързо.

Проблеми, решени чрез многопоточност

Многонишковостта всъщност е изобретена за постигане на две важни цели:
  1. Правете няколко неща едновременно.

    В примера по-горе различни нишки (членове на семейството) извършват няколко действия паралелно: измиват чинии, отиват до магазина и опаковат нещата.

    Можем да предложим пример, по-тясно свързан с програмирането. Да предположим, че имате програма с потребителски интерфейс. Когато щракнете върху „Продължи“ в програмата, трябва да се извършат някои изчисления и потребителят трябва да види следния екран. Ако тези действия бяха извършени последователно, тогава програмата просто щеше да увисне, след като потребителят щракне върху бутона „Продължи“. Потребителят ще вижда екрана с екрана с бутона „Продължи“, докато програмата извърши всички вътрешни изчисления и достигне частта, в която потребителският интерфейс се обновява.

    Е, предполагам, че ще изчакаме няколко minutesи!

    Многопоточност в Java: Howво е това, предимствата и често срещаните клопки - 3

    Или можем да преработим нашата програма or, Howто казват програмистите, да я „паралелизираме“. Нека извършим нашите изчисления в една нишка и да начертаем потребителския интерфейс в друга. Повечето компютри имат достатъчно ресурси за това. Ако изберем този маршрут, тогава програмата няма да замръзне и потребителят ще се движи плавно между екраните, без да се притеснява Howво се случва вътре. Едното не пречи на другото :)

  2. Извършвайте изчисления по-бързо.

    Тук всичко е много по-просто. Ако нашият процесор има множество ядра, а повечето процесори днес имат, тогава няколко ядра могат да се справят с нашия списък от задачи паралелно. Очевидно, ако трябва да изпълним 1000 задачи и всяка от тях отнема една секунда, едно ядро ​​може да завърши списъка за 1000 секунди, две ядра за 500 секунди, три за малко повече от 333 секунди и т.н.

Но Howто вече прочетохте в този урок, днешните системи са много интелигентни и дори на едно изчислително ядро ​​могат да постигнат паралелизъм or по-скоро псевдопаралелизъм, при който задачите се изпълняват последователно. Нека прехвърлим общите неща към спецификите и да се запознаем с най-важния клас в многонишковата библиотека на Java — java.lang.Thread. Строго погледнато, нишките на Java са представени от екземпляри на класа Thread . Това означава, че за да създадете и стартирате 10 нишки, имате нужда от 10 екземпляра от този клас. Нека напишем най-простия пример:

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ъв абстрактен смисъл. Има два специфични проблема, които многонишковостта може да създаде: безизходица и състезателни условия. Безизходица е ситуация, при която множество нишки чакат ресурси, държани една от друга, и нито една от тях не може да продължи да работи. Ще говорим повече за това в следващите уроци. Следният пример ще е достатъчен за сега: Многопоточност в Java: Howво представлява, ползите и често срещаните клопки - 4Представете си, че Thread-1 взаимодейства с няHowъв Object-1 и че Thread-2 взаимодейства с Object-2. Освен това програмата е написана така, че:
  1. Thread-1 спира да взаимодейства с Object-1 и превключва към Object-2 веднага щом Thread-2 спре да взаимодейства с Object-2 и превключва към Object-1.
  2. Thread-2 спира да взаимодейства с Object-2 и превключва към Object-1 веднага щом Thread-1 спре да взаимодейства с Object-1 и превключва към Object-2.
Дори и без дълбоко разбиране на многонишковостта, можете лесно да видите, че нищо няма да се случи. Нишките никога няма да разменят местата си и ще чакат една друга завинаги. Грешката изглежда очевидна, но в действителност не е така. Можете лесно да направите това в програма. Ще разгледаме примери за code, който причинява блокиране в следващите уроци. Между другото, Quora има страхотен пример от реалния живот , който обяснява Howво е безизходицатае. „В някои щати в Индия няма да ви продадат земеделска земя, освен ако не сте регистриран фермер. Но няма да те регистрират като земеделски производител, ако нямаш земеделска земя“. Страхотен! Какво да кажем?! :) Сега да поговорим за условията на състезанието. Състезание е грешка в дизайна на многонишкова система or приложение, където работата на системата or приложението зависи от реда, в който се изпълняват части от codeа. Спомнете си нашия пример, в който започнахме теми:

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 Това рутинна комедия ли е? :) И всичко това, защото работата на нашата програма зависи от реда на изпълнение на нишките. При най-малкото нарушение на необходимата последователност, нашата кухня се превръща в ад, а луд робот унищожава всичко около нея. Това също е често срещан проблем при многопоточното програмиране. Ще чуете за това повече от веднъж. В заключение на този урок бих искал да препоръчам книга за многопоточността. Многопоточност в Java: Howво е това, предимствата и често срещаните клопки - 6"Java Concurrency на практика" е написана през 2006 г., но не е загубила своята актуалност. Той е посветен на многопоточното програмиране на Java — от основите до най-често срещаните грешки и антишаблони. Ако някой ден решите да станете многонишков гуру, тази книга трябва да прочетете. Ще се видим в следващите уроци! :)
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION