CodeGym /Java blog /Véletlen /Jobb együtt: Java és a Thread osztály. I. rész – A végreh...
John Squirrels
Szint
San Francisco

Jobb együtt: Java és a Thread osztály. I. rész – A végrehajtás szálai

Megjelent a csoportban

Bevezetés

A Multithreading a kezdetektől fogva beépült a Java-ba. Tehát, nézzük röviden ezt a dolgot, amit többszálúnak neveznek. Jobb együtt: Java és a Thread osztály.  I. rész – A végrehajtás szálai – 1Tájékoztatásul az Oracle hivatalos leckét vesszük: " Lecke: A "Hello World!" alkalmazás ". Kissé módosítjuk Hello World programunk kódját az alábbiak szerint:

class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsa program indításakor átadott bemeneti paraméterek tömbje. Mentse ezt a kódot egy olyan fájlba, amelynek neve megegyezik az osztály nevével, és amelynek kiterjesztése .java. Fordítsa le a javac segédprogrammal: javac HelloWorldApp.java. Ezután futtatjuk a kódunkat valamilyen paraméterrel, például "Roger": java HelloWorldApp Roger Jobb együtt: Java és a Thread osztály.  I. rész – A végrehajtás szálai – 2A kódunknak jelenleg komoly hibája van. Ha nem ad át egyetlen argumentumot sem (vagyis csak a "java HelloWorldApp" parancsot hajtja végre), akkor hibaüzenetet kapunk:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
Kivétel (pl. hiba) történt a "main" nevű szálban. Szóval, a Java-nak vannak szálai? Itt kezdődik a mi utunk.

Java és szálak

Ahhoz, hogy megértsük, mi a szál, meg kell értenünk, hogyan indul el egy Java program. Változtassuk meg a kódunkat az alábbiak szerint:

class HelloWorldApp {
    public static void main(String[] args) {
		while (true) { 
			// Do nothing
		}
	}
}
Most fordítsuk újra a -val javac. A kényelem kedvéért a Java kódunkat egy külön ablakban futtatjuk. Windows rendszeren ez a következőképpen tehető meg: start java HelloWorldApp. Most a jps segédprogramot fogjuk használni , hogy megnézzük, milyen információkat tud közölni a Java: Jobb együtt: Java és a Thread osztály.  I. rész – A végrehajtás szálai – 3Az első szám a PID vagy a folyamatazonosító. Mi az a folyamat?

A process is a combination of code and data sharing a common virtual address space.
A folyamatok során a különböző programok futásuk során elkülönülnek egymástól: minden alkalmazás a saját memóriaterületét használja anélkül, hogy más programokat zavarna. Ha többet szeretne megtudni, azt javaslom, hogy olvassa el ezt az oktatóanyagot: Folyamatok és szálak . Egy folyamat nem létezhet szál nélkül, tehát ha létezik folyamat, akkor annak legalább egy szála van. De hogyan jön ez elő a Java-ban? Amikor elindítunk egy Java programot, a végrehajtás a metódussal kezdődik main. Mintha belelépnénk a programba, ezért ezt a speciális mainmódszert nevezzük belépési pontnak. A mainmetódusnak mindig "public static void"-nak kell lennie, hogy a Java virtuális gép (JVM) elkezdhesse végrehajtani programunkat. További információ: Miért statikus a Java fő metódus?. Kiderült, hogy a Java indító (java.exe vagy javaw.exe) egy egyszerű C-alkalmazás: betölti a különböző DLL-eket, amelyek valójában a JVM-et tartalmazzák. A Java indító a Java Native Interface (JNI) hívások meghatározott készletét hajtja végre. A JNI egy olyan mechanizmus, amely összekapcsolja a Java virtuális gép világát a C++ világával. Tehát az indító nem maga a JVM, hanem egy mechanizmus a betöltésére. Ismeri a végrehajtandó helyes parancsokat a JVM elindításához. Tudja, hogyan kell használni a JNI-hívásokat a szükséges környezet beállításához. Ennek a környezetnek a beállítása magában foglalja a fő szál létrehozását, amelyet "fő"-nek neveznek. A Java folyamatban lévő szálak jobb szemléltetésére a jvisualvm-et használjukeszköz, amely a JDK-hoz tartozik. Egy folyamat pid-jének ismeretében azonnal láthatjuk az adott folyamatra vonatkozó információkat: jvisualvm --openpid <process id> Jobb együtt: Java és a Thread osztály.  I. rész – A végrehajtás szálai – 4Érdekes módon minden szálnak megvan a saját külön területe a folyamat számára lefoglalt memóriában. Ezt a memóriastruktúrát veremnek nevezzük. Egy köteg keretekből áll. A keret egy metódus aktiválását jelöli (egy befejezetlen metódushívás). Egy keret StackTraceElementként is ábrázolható (lásd a StackTraceElement Java API-ját ). Az egyes szálakhoz lefoglalt memóriáról itt találhat további információt: " Hogyan foglal le a Java (JVM) verem minden szálhoz ". Ha megnézi a Java API-t , és rákeres a "Thread" szóra, a java.lang.Thread fájlt találja.osztály. Ez az az osztály, amely egy szálat képvisel Java-ban, és ezzel kell dolgoznunk. Jobb együtt: Java és a Thread osztály.  I. rész – A végrehajtás szálai – 5

java.lang.Szál

Java nyelven egy szálat az osztály egy példánya képvisel java.lang.Thread. Azonnal meg kell értenie, hogy a Thread osztály példányai önmagukban nem végrehajtási szálak. Ez csak egyfajta API a JVM és az operációs rendszer által kezelt alacsony szintű szálakhoz. Amikor elindítjuk a JVM-et a Java indító segítségével, létrehoz egy main"fő" nevű szálat és néhány további háztartási szálat. Ahogy azt a JavaDoc a Thread osztályhoz tartalmazza: When a Java Virtual Machine starts up, there is usually a single non-daemon thread. Kétféle szál létezik: démonok és nem démonok. A démonszálak háttér (háztartási) szálak, amelyek a háttérben dolgoznak. A "démon" szó Maxwell démonára utal. Ebben a Wikipédia cikkben többet megtudhat . Ahogy a dokumentációban is szerepel, a JVM mindaddig folytatja a program (folyamat) végrehajtását, amíg:
  • A Runtime.exit() metódus meghívásra kerül
  • Minden NEM démon szál befejezi a munkáját (hiba nélkül vagy kivételekkel)
Ebből egy fontos részlet következik: a démonszálak bármikor megszakíthatók. Ennek eredményeként nincs garancia az adataik sértetlenségére. Ennek megfelelően a démonszálak alkalmasak bizonyos háztartási feladatokra. Például a Java-nak van egy szála, amely a metódushívások feldolgozásáért felel finalize(), azaz a szemétgyűjtőhöz (gc) kapcsolódó szálakért. Minden szál egy csoport ( ThreadGroup ) része . A csoportok pedig részei lehetnek más csoportoknak, egy bizonyos hierarchiát vagy struktúrát alkotva.

public static void main(String[] args) {
	Thread currentThread = Thread.currentThread();
	ThreadGroup threadGroup = currentThread.getThreadGroup();
	System.out.println("Thread: " + currentThread.getName());
	System.out.println("Thread Group: " + threadGroup.getName());
	System.out.println("Parent Group: " + threadGroup.getParent().getName());
}
A csoportok rendet teremtenek a szálkezelésben. A csoportokon kívül a szálak saját kivételkezelővel is rendelkeznek. Vessen egy pillantást egy példára:

public static void main(String[] args) {
	Thread th = Thread.currentThread();
	th.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
		@Override
		public void uncaughtException(Thread t, Throwable e) {
			System.out.println("An error occurred: " + e.getMessage());
		}
	});
    System.out.println(2/0);
}
A nullával való osztás hibát okoz, amelyet a kezelő elkap. Ha nem ad meg saját kezelőt, akkor a JVM meghívja az alapértelmezett kezelőt, amely a kivétel veremnyomkövetését adja ki az StdError-nak. Minden szálnak van prioritása is. A prioritásokról ebben a cikkben olvashat bővebben: Java Thread Priority in Multithreading .

Egy szál létrehozása

Ahogy a dokumentációban is szerepel, kétféleképpen hozhatunk létre szálat. Az első módszer a saját alosztály létrehozása. Például:

public class HelloWorld{
    public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello, World!");  
        }
    }
    
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
    }
}
Mint látható, a feladat munkája a metódusban történik run(), de maga a szál elindul a metódusban start(). Ne keverje össze ezeket a módszereket: ha közvetlenül hívjuk az r un()metódust, akkor nem indul új szál. Ez az a start()módszer, amely arra kéri a JVM-et, hogy hozzon létre egy új szálat. Ez az opció, ahol a szálat örököljük, már rossz abból a szempontból, hogy az osztályhierarchiánkban szerepel a Thread. A második hátrány, hogy kezdjük megsérteni az „egyedülálló felelősség” elvét. Vagyis a mi osztályunk egyidejűleg felelős a szál vezérléséért és az ebben a szálban végrehajtandó feladatokért. Mi a helyes út? A válasz ugyanabban a módszerben található run(), amelyet felülírunk:

public void run() {
	if (target != null) {
		target.run();
	}
}
Íme targetnéhány java.lang.Runnable, amelyet átadhatunk a Thread osztály példányának létrehozásakor. Ez azt jelenti, hogy ezt tehetjük:

public class HelloWorld{
    public static void main(String[] args) {
        Runnable task = new Runnable() {
            public void run() {
                System.out.println("Hello, World!");
            } 
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
Runnablea Java 1.8 óta funkcionális felület is. Ez lehetővé teszi, hogy még szebb kódot írjunk egy szál feladatához:

public static void main(String[] args) {
	Runnable task = () -> { 
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

Következtetés

Remélem, ez a vita tisztázza, mi az a szál, hogyan jönnek létre a szálak, és milyen alapvető műveleteket lehet végrehajtani a szálakkal. A következő részben megpróbáljuk megérteni, hogyan hatnak egymásra a szálak, és megvizsgáljuk a szálak életciklusát. Jobb együtt: Java és a Thread osztály. II. rész – Szinkronizálás Jobb együtt: Java és a Thread osztály. III. rész – Interakció Jobb együtt: Java és a szál osztály. IV. rész – Hívható, jövő és barátok Jobb együtt: Java és a szál osztály. V. rész – Végrehajtó, ThreadPool, Fork/Join Better together: Java és a Thread osztály. VI. rész – Tüzet el!
Hozzászólások
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION