Többszálú megoldással megoldott problémák
A többszálas megoldást valójában két fontos cél elérése érdekében találták ki:-
Csinálj több dolgot egyszerre.
A fenti példában a különböző szálak (családtagok) több műveletet hajtottak végre párhuzamosan: mosogattak, elmentek a boltba, és összepakoltak.
A programozáshoz közelebbről is tudunk példát mondani. Tegyük fel, hogy van egy felhasználói felülettel rendelkező programja. Amikor a programban a „Tovább” gombra kattint, néhány számításnak meg kell történnie, és a felhasználónak a következő képernyőt kell látnia. Ha ezeket a műveleteket egymás után hajtják végre, akkor a program lefagy, miután a felhasználó a „Folytatás” gombra kattint. A felhasználó addig fogja látni a „Folytatás” gomb képernyőjét, amíg a program elvégzi az összes belső számítást és el nem éri azt a részt, ahol a felhasználói felület frissül.
Nos, azt hiszem, várunk egy pár percet!
Vagy átdolgozhatjuk a programunkat, vagy ahogy a programozók mondják, „párhuzamba állíthatjuk”. Végezzük el a számításainkat az egyik szálon, és rajzoljuk meg a felhasználói felületet egy másik szálon. A legtöbb számítógép elegendő erőforrással rendelkezik ehhez. Ha ezt az utat választjuk, akkor a program nem fagy le, és a felhasználó gördülékenyen mozog a képernyők között anélkül, hogy aggódnia kellene a benti események miatt. Egyik nem zavarja a másikat :)
-
Gyorsabban végezzen számításokat.
Itt minden sokkal egyszerűbb. Ha a processzorunk több maggal rendelkezik, és a legtöbb processzor ma is rendelkezik, akkor több mag képes párhuzamosan kezelni a feladatlistánkat. Nyilvánvalóan, ha 1000 feladatot kell végrehajtanunk, és mindegyik egy másodpercet vesz igénybe, akkor egy mag 1000 másodperc alatt, két mag 500 másodperc alatt, három valamivel több, mint 333 másodperc alatt fejezheti be a listát, stb.
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("I'm Thread! My name is " + getName());
}
}
Szálak létrehozásához és futtatásához létre kell hoznunk egy osztályt, amely örökli a java.lang fájlt . Szálosztályt , és felülírja a run() metódust. Ez utóbbi követelmény nagyon fontos. A run() metódusban határozzuk meg a szálunk végrehajtásának logikáját. Most, ha létrehozzuk és futtatjuk a MyFirstThread példányát , a run() metódus egy sort jelenít meg egy névvel: a getName() metódus megjeleníti a szál „rendszer” nevét, amely automatikusan hozzá van rendelve. De miért beszélünk próbaképpen? Hozzunk létre egyet, és megtudjuk!
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
Konzol kimenet: Thread vagyok! A nevem Thread-2 Thread vagyok! A nevem Thread-1 Thread vagyok! A nevem Thread-0 Thread vagyok! A nevem Thread-3 Thread vagyok! A nevem Thread-6 Thread vagyok! A nevem Thread-7 Thread vagyok! A nevem Thread-4 Thread vagyok! A nevem Thread-5 Thread vagyok! A nevem Thread-9 Thread vagyok! A nevem Thread-8 Hozzunk létre 10 szálat ( MyFirstThread objektumokat, amelyek öröklik a Thread -t ), és indítsuk el őket a start() metódus meghívásával minden objektumon. A start() metódus meghívása után a run() metódusban szereplő logika végrehajtásra kerül. Megjegyzés: a szálnevek nem sorrendben vannak. Furcsa, hogy nem egymás után voltak:, Thread-1 , Thread-2 , és így tovább? Amint megtörténik, ez egy példa arra az időszakra, amikor a „szekvenciális” gondolkodás nem fér bele. A probléma az, hogy csak 10 szál létrehozásához és futtatásához adtunk parancsokat. A szálütemező, egy speciális operációs rendszer-mechanizmus, határozza meg a végrehajtási sorrendjüket. Pontos tervezése és döntéshozatali stratégiája olyan mély viták témája, amelyekbe most nem merülünk bele. A legfontosabb, hogy ne feledje, hogy a programozó nem tudja szabályozni a szálak végrehajtási sorrendjét. A helyzet súlyosságának megértéséhez próbálja meg még néhányszor futtatni a main() metódust a fenti példában. Konzol kimenet második futtatáskor: Thread vagyok! A nevem Thread-0 Thread vagyok! A nevem Thread-4 Thread vagyok! A nevem Thread-3 Thread vagyok! A nevem Thread-2 Thread vagyok! A nevem Thread-1 Thread vagyok! A nevem Thread-5 Thread vagyok! A nevem Thread-6 Thread vagyok! A nevem Thread-8 Thread vagyok! A nevem Thread-9 Thread vagyok! A nevem Thread-7 konzol kimenete a harmadik futtatásból: Thread vagyok! A nevem Thread-0 Thread vagyok! A nevem Thread-3 Thread vagyok! A nevem Thread-1 Thread vagyok! A nevem Thread-2 Thread vagyok! A nevem Thread-6 Thread vagyok! A nevem Thread-4 Thread vagyok! A nevem Thread-9 Thread vagyok! A nevem Thread-5 Thread vagyok! A nevem Thread-7 Thread vagyok! A nevem Thread-8
Többszálú feldolgozás okozta problémák
A könyvekkel kapcsolatos példánkban láthatta, hogy a többszálú megoldás nagyon fontos feladatokat old meg, és gyorsabbá teheti programjainkat. Sokszor gyorsabban. De a többszálú feldolgozást nehéz témának tartják. Valójában, ha nem megfelelően használják, problémákat okoz, ahelyett, hogy megoldaná azokat. Amikor azt mondom, hogy „problémákat okoz”, nem valamilyen elvont értelemben értem. A többszálú használat két konkrét problémát okozhat: holtpont és versenyfeltételek. A holtpont olyan helyzet, amikor több szál vár egymás által birtokolt erőforrásokra, és egyikük sem futhat tovább. Erről bővebben a következő leckéken fogunk beszélni. Egyelőre elegendő a következő példa:
- Az 1-es szál leállítja az 1-es objektummal való interakciót, és átvált a 2-es objektumra, amint a 2-es szál abbahagyja az Object-2-vel való interakciót, és átvált az Object-1-re.
- A 2. szál leállítja a 2. objektum interakcióját, és átvált az 1. objektumra, amint az 1. szál abbahagyja az 1. objektum interakcióját, és átvált az Object-2-re.
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();
}
}
}
Most képzeljük el, hogy a program egy ételt főző robot működtetéséért felelős! A 0-ás szál kiveszi a tojásokat a hűtőből. Az 1-es szál bekapcsolja a tűzhelyet. A 2. szál kap egy serpenyőt, és felteszi a tűzhelyre. A 3. szál meggyújtja a kályhát. A 4-es szál olajat önt a serpenyőbe. Az 5-ös szál feltöri a tojásokat és beleönti a serpenyőbe. A 6-os szál a szemetesbe dobja a tojáshéjakat. A 7-es szál eltávolítja a főtt tojást az égőből. A 8-as szál tányérra teszi a főtt tojást. A 9-es szál mosogat. Tekintse meg programunk eredményét: Szál végrehajtva: Thread-0 Szál végrehajtva: Thread-2 Szál végrehajtva Thread-1 Szál végrehajtva: Thread-4 Szál végrehajtva: Thread-9 Szál végrehajtva: Thread-5 Szál végrehajtva: Thread-8 Thread végrehajtva: Thread-7 Szál végrehajtva: Thread-3 Ez egy komédia rutin? :) És mindez azért, mert a programunk munkája a szálak végrehajtási sorrendjétől függ. A szükséges sorrend legkisebb megsértése esetén konyhánk pokollá változik, és egy őrült robot mindent elpusztít maga körül. Ez a többszálú programozásban is gyakori probléma. Többször hallani fogsz róla. A lecke befejezéseként szeretnék egy könyvet ajánlani a többszálú feldolgozásról. 
GO TO FULL VERSION