Grupe niti i prioriteti niti (manje poznato znanje)
Java pruža klasu ThreadGroup za kreiranje grupe srodnih niti, čime se upravljanje grupom niti čini lakšim. Svaka Java nit ima prioritet koji utiče na redosled po kom joj operativni sistem dodeljuje processorsko vreme.
U ovom tekstu ćemo posebno predstaviti grupe niti i prioritete niti.
Grupa niti (ThreadGroup)
Java koristi ThreadGroup da predstavi grupu niti; preko grupe niti možemo vršiti grupnu kontrolu nad nitima.
Odnos između ThreadGroup i Thread-a je jednostavan i direktan kao što ime kaže: svaki Thread obavezno postoji unutar jednog ThreadGroup-a i ne može da postoji nezavisno od ThreadGroup-a. Ime niti koja izvršava metodu main() je main; ako se pri new Thread ne navede eksplicitno, podrazumevano će se grupa niti roditeljske niti (niti koja trenutno izvršava new Thread) postaviti kao sopstvena grupa niti.
Primer koda:
Thread testThread = new Thread(() -> {
System.out.println("testThread ime trenutne grupe niti: " +
Thread.currentThread().getThreadGroup().getName());
System.out.println("testThread ime niti: " +
Thread.currentThread().getName());
});
testThread.start();
System.out.println("Ime grupe niti u kojoj se izvršava main: " + Thread.currentThread().getThreadGroup().getName());
System.out.println("Ime niti koja izvršava metodu main: " + Thread.currentThread().getName());Izlazni rezultat:
Ime grupe niti u kojoj se izvršava main: main
testThread ime trenutne grupe niti: main
testThread ime niti: Thread-0
Ime niti koja izvršava metodu main: mainThreadGroup je standardna drvolika struktura sa referencama nadole. Ovakav dizajn sprečava da „nadređenu" nit zadržava referenca „podređene" niti, čime bi se sprečilo efikasno GC prikupljanje.
Uobičajene metode i struktura podataka grupe niti
Dobijanje imena grupe niti trenutne niti
Thread.currentThread().getThreadGroup().getName()Kopiranje grupe niti
// Dobijanje trenutne grupe niti
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
// Kopiranje grupe niti u niz niti (dobijanje Thread informacija)
Thread[] threads = new Thread[threadGroup.activeCount()];
threadGroup.enumerate(threads);Jedinstvena obrada izuzetaka grupe niti
// Kreiranje grupe niti i redefinisanje izuzetka
ThreadGroup group = new ThreadGroup("testGroup") {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t.getName() + ": " + e.getMessage());
}
};
// Testiranje izuzetka
Thread thread = new Thread(group, () -> {
// Baca unchecked izuzetak
throw new RuntimeException("test izuzetak");
});
// Pokretanje niti
thread.start();Struktura podataka grupe niti
Grupa niti može da sadrži i druge grupe niti, a ne samo niti. Hajde prvo da pogledamo članske promenljive u izvornom kodu klase ThreadGroup.
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
private final ThreadGroup parent; // roditelj ThreadGroup
String name; // Naziv ThreadGroup-a
int maxPriority; // Maksimalni prioritet
boolean destroyed; // Da li je uništen
boolean daemon; // Da li je daemon nit
boolean vmAllowSuspension; // Da li se može prekinuti
int nUnstartedThreads = 0; // Niti koje još nisu pokrenute
int nthreads; // Broj niti u ThreadGroup-u
Thread threads[]; // Niti u ThreadGroup-u
int ngroups; // Broj grupa niti
ThreadGroup groups[]; // Niz grupa niti
}Zatim da pogledamo konstruktor:
// Privatni konstruktor
private ThreadGroup() {
this.name = "system";
this.maxPriority = Thread.MAX_PRIORITY;
this.parent = null;
}
// Podrazumevano se trenutni ThreadGroup uzima kao roditeljski ThreadGroup; roditeljska grupa niti nove grupe niti je grupa niti niti koja se trenutno izvršava.
public ThreadGroup(String name) {
this(Thread.currentThread().getThreadGroup(), name);
}
// Konstruktor
public ThreadGroup(ThreadGroup parent, String name) {
this(checkParentAccess(parent), parent, name);
}
// Privatni konstruktor, glavni konstruktor
private ThreadGroup(Void unused, ThreadGroup parent, String name) {
this.name = name;
this.maxPriority = parent.maxPriority;
this.daemon = parent.daemon;
this.vmAllowSuspension = parent.vmAllowSuspension;
this.parent = parent;
parent.add(this);
}Treći konstruktor poziva metodu checkParentAccess; hajde da pogledamo njen izvorni kod:
// Provera parent ThreadGroup-a
private static Void checkParentAccess(ThreadGroup parent) {
parent.checkAccess();
return null;
}
// Proverava da li trenutno izvršavana nit ima dozvolu za izmenu grupe niti
public final void checkAccess() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkAccess(this);
}
}Ovde je uključena klasa SecurityManager. To je Java menadžer bezbednosti koji omogućava aplikaciji da, pre izvršenja operacije koja može biti nebezbedna ili osetljiva, utvrdi šta je ta operacija i da li je dozvoljeno izvršiti je u tom kontekstu izvršenja.
Na primer, uvođenje biblioteke treće strane ne garantuje njenu bezbednost.
Zapravo, klasa Thread takođe ima metodu checkAccess, ali ona služi da proveri da li trenutno izvršavana nit ima dozvolu da izmeni tu instancu niti nad kojom se poziva. (Determines if the currently running thread has permission to modify this thread.)
Da sumiramo: grupa niti je drvolika struktura; ispod svake grupe niti može biti više niti ili grupa niti. Grupa niti može da ima ulogu jedinstvene kontrole prioriteta niti i provere dozvola niti.
Prioritet niti
Prioritet niti se može zadati, u opsegu 1~10. Ali ne podržavaju svi operativni sistemi podelu na 10 nivoa prioriteta (na primer, neki operativni sistemi podržavaju samo 3 nivoa: niski, srednji, visoki). Java operativnom sistemu daje samo referentnu vrednost prioriteta; konačan prioritet u operativnom sistemu i dalje određuje operativni sistem.
Podrazumevani prioritet Java niti je 5. Redosled izvršenja niti određuje rasporedač, a prioritet niti se postavlja pre nego što nit bude pozvana.
Obično, niti višeg prioriteta će imati veću verovatnoću da budu izvršene od niti nižeg prioriteta. Metoda setPriority() klase Thread može se koristiti za zadavanje prioriteta niti.
Thread a = new Thread();
System.out.println("Podrazumevani prioritet niti: " + a.getPriority());
Thread b = new Thread();
b.setPriority(10);
System.out.println("Prioritet niti nakon postavljanja: " + b.getPriority());Izlazni rezultat:
Podrazumevani prioritet niti: 5
Prioritet niti nakon postavljanja: 10Pošto postoji 10 nivoa za zadavanje prioriteta niti, da li možemo u poslovnoj implementaciji primeniti ovaj metod da odredimo redosled izvršenja niti?
Za to pitanje je odgovor: Ne!
Prioritet u Javi nije naročito pouzdan. Prioritet niti koji se postavi u Java programu samo je predlog operativnom sistemu, koji operativni sistem ne mora da usvoji. Stvarni redosled poziva određuje algoritam raspodele niti operativnog sistema.
Hajde da ovo potvrdimo kodom:
static class MyThread extends Thread {
@Override
public void run() {
// Ispisuje ime i prioritet trenutne niti
System.out.println("MyThread trenutna nit: " + Thread.currentThread().getName()
+ ", prioritet: " + Thread.currentThread().getPriority());
}
}
public static void main(String[] args) {
// Kreira 10 niti, radi od 1-10, prioritet od 1-10
for (int i = 1; i <= 10; i++) {
Thread thread = new MyThread();
thread.setName("Nit " + i);
thread.setPriority(i);
thread.start();
}
}Pri pokretanju ovog programa, ponekad se izvršava po prioritetu, a ponekad ne. Jedan od izlaza je bio:
MyThread trenutna nit: Nit 2, prioritet: 2
MyThread trenutna nit: Nit 4, prioritet: 4
MyThread trenutna nit: Nit 3, prioritet: 3
MyThread trenutna nit: Nit 5, prioritet: 5
MyThread trenutna nit: Nit 1, prioritet: 1
MyThread trenutna nit: Nit 6, prioritet: 6
MyThread trenutna nit: Nit 7, prioritet: 7
MyThread trenutna nit: Nit 8, prioritet: 8
MyThread trenutna nit: Nit 9, prioritet: 9
MyThread trenutna nit: Nit 10, prioritet: 10Java pruža rasporedač niti koji nadgleda i kontroliše niti u stanju RUNNABLE.
- Strategija raspodele niti koristi preemptivni način; niti višeg prioriteta imaće veću verovatnoću da budu izvršene pre niti nižeg prioriteta.
- Pri istom prioritetu, primenjuje se princip „prvi-došao-prvi-uslužen".
- Svaki Java program ima podrazumevanu glavnu nit — to je prva nit pokrenuta putem JVM-a — nit main.
Postoji i posebna vrsta niti koja se zove daemon nit (Daemon). Podrazumevani prioritet daemon niti je niži.
- Ako je neka nit daemon nit, onda kada se završe sve ne-daemon niti, ta daemon nit će se automatski završiti.
- Kada se završe sve ne-daemon niti, daemon niti se automatski zatvaraju, čime se izbegava neprijatnost daljeg zatvaranja podređenih niti.
- Nit je podrazumevano ne-daemon nit; može se podesiti kao daemon nit putem metode setDaemon klase Thread.
Odnos između grupe niti i prioriteta niti
Ranije smo rekli da jedna nit obavezno postoji unutar jedne grupe niti. Šta se onda dešava kada se prioriteti niti i grupe niti ne poklapaju? Hajde da to proverimo:
// Kreiranje grupe niti
ThreadGroup group = new ThreadGroup("testGroup");
// Prioritet grupe niti se postavlja na 7
group.setMaxPriority(7);
// Kreiranje niti i dodavanje te niti u group
Thread thread = new Thread(group, "test-thread");
// Pokušaj postavljanja prioriteta niti na 10
thread.setPriority(10);
// Ispis prioriteta grupe niti i prioriteta niti
System.out.println("Prioritet grupe niti je: " + group.getMaxPriority());
System.out.println("Prioritet niti je: " + thread.getPriority());Izlaz:
Prioritet grupe niti je: 7
Prioritet niti je: 7Dakle, ako je prioritet neke niti veći od maksimalnog prioriteta grupe niti kojoj nit pripada, prioritet te niti će prestati da važi, a zameniće ga maksimalni prioritet grupe niti.
Rezime
Java pruža klasu ThreadGroup za kreiranje grupe srodnih niti, čime se upravljanje grupom niti čini lakšim; svaka Java nit ima prioritet koji utiče na redosled po kom joj operativni sistem dodeljuje processorsko vreme.
Urednik: Chenmo Wang Er. Originalni sadržaj potiče iz ovog repozitorijuma mog prijatelja Xiaoqi Yinghuochong-a: Java višenitnost jednostavno objašnjena, toplo preporučujem.
