Giriş
Multithreading Java-da ilk günlərdən bəri qurulmuşdur. Beləliklə, gəlin multithreading nə ilə əlaqəli olduğuna qısaca nəzər salaq.
Başlanğıc nöqtəsi olaraq Oracle-dan rəsmi dərsi götürək: "
Dərs: "Salam Dünya!" Tətbiqi ". Hello World tətbiqimizin kodunu bir az aşağıdakılara dəyişək:
class HelloWorldApp {
public static void main(String[] args) {
System.out.println("Hello, " + args[0]);
}
}
args
proqram başlayanda ötürülən giriş parametrləri massividir. Gəlin bu kodu sinfin adına və genişlənməsinə uyğun gələn adla faylda saxlayaq
.java
.
Gəlin javac yardım proqramından istifadə edərək kompilyasiya edək :
javac HelloWorldApp.java
Bundan sonra kodumuza hansısa parametrlə zəng edin, məsələn, Roger:
java HelloWorldApp Roger
İndi kodumuzda ciddi bir qüsur var. Heç bir arqumenti qəbul etməsək (yəni, sadəcə java HelloWorldApp-ı yerinə yetirsək), xəta alacağıq:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at HelloWorldApp.main(HelloWorldApp.java:3)
adlı mövzuda istisna (yəni xəta) baş verdi
main
. Belə çıxır ki, Java-da bir növ mövzu var? Səyahətimiz buradan başlayır.
Java və mövzular
Bir mövzunun nə olduğunu başa düşmək üçün Java proqramının necə işə salındığını başa düşməlisiniz. Kodumuzu aşağıdakı kimi dəyişdirək:
class HelloWorldApp {
public static void main(String[] args) {
while (true) {
}
}
}
İndi javac istifadə edərək onu yenidən kompilyasiya edək. Sonra, rahatlıq üçün Java kodumuzu ayrıca pəncərədə işlədəcəyik. Windows-da bunu belə edə bilərsiniz:
start java HelloWorldApp
. İndi
jps yardım proqramından istifadə edərək Java-nın bizə hansı məlumatı deyəcəyini görək:
Birinci nömrə PID və ya Proses ID-sidir, proses identifikatorudur. Proses nədir?
Процесс — это совокупность codeа и данных, разделяющих общее виртуальное addressное пространство.
Proseslərin köməyi ilə müxtəlif proqramların icrası bir-birindən təcrid olunur: hər bir proqram digər proqramlara müdaxilə etmədən öz yaddaş sahəsindən istifadə edir. Məqaləni daha ətraflı oxumağı məsləhət görürəm: "
https://habr.com/post/164487/ ". Proses iplərsiz mövcud ola bilməz, ona görə də proses varsa, onda ən azı bir iplik mövcuddur. Java-da bu necə baş verir? Java proqramını işə saldığımız zaman onun icrası
main
. Biz bir növ proqrama daxil oluruq, ona görə də bu xüsusi metoda
main
giriş nöqtəsi və ya “giriş nöqtəsi” deyilir. Metod
main
həmişə elə olmalıdır
public static void
ki, Java Virtual Maşın (JVM) proqramımızı icra etməyə başlaya bilsin. Ətraflı məlumat üçün "
Java əsas metodu niyə statikdir? "-a baxın. Məlum oldu ki, java başlatma qurğusu (java.exe və ya javaw.exe) sadə proqramdır (sadə C proqramı): o, əslində JVM olan müxtəlif DLL-ləri yükləyir. Java başlatma proqramı xüsusi Java Native Interface (JNI) zəngləri dəsti edir. JNI Java Virtual Machine dünyası ilə C++ dünyası arasında körpü yaradan mexanizmdir. Məlum oldu ki, başlatma cihazı JVM deyil, onun yükləyicisidir. JVM-i işə salmaq üçün düzgün əmrləri bilir. JNI zənglərindən istifadə edərək bütün lazımi mühiti necə təşkil etməyi bilir. Ətraf mühitin bu təşkili adətən adlanan əsas ipin yaradılmasını da əhatə edir
main
. Java prosesində hansı mövzuların yaşadığını daha aydın görmək üçün JDK-ya daxil olan
jvisualvm proqramından istifadə edirik. Prosesin pidini bilməklə, biz onun haqqında məlumatları dərhal aça bilərik:
jvisualvm --openpid айдипроцесса
Maraqlıdır ki, hər bir ipin proses üçün ayrılmış yaddaşda öz ayrıca sahəsi var. Bu yaddaş strukturu stack adlanır. Yığın çərçivələrdən ibarətdir. Çərçivə metodu çağırma nöqtəsi, icra nöqtəsidir. Çərçivə StackTraceElement kimi də təmsil oluna bilər (bax:
StackTraceElement üçün Java API ).
Burada hər mövzuya ayrılmış yaddaş haqqında daha çox oxuya bilərsiniz .
Java API- yə baxsaq və Thread sözünü axtarsaq, görərik ki,
java.lang.Thread sinfi var . Java-da axını təmsil edən bu sinifdir və biz bununla işləməliyik.
java.lang.Mövzu
Java-da mövzu sinifin bir nümunəsi kimi təmsil olunur
java.lang.Thread
. Java-dakı Thread sinifinin nümunələrinin özləri mövzu olmadığını dərhal başa düşməyə dəyər. Bu, JVM və əməliyyat sistemi tərəfindən idarə olunan aşağı səviyyəli mövzular üçün sadəcə bir növ API-dir. Biz Java başlatma qurğusundan istifadə edərək JVM-i işə saldığımız zaman o, adı
main
və daha bir neçə xidmət ipi olan əsas ip yaradır. Thread sinfinin JavaDoc-da qeyd edildiyi kimi:
When a Java Virtual Machine starts up, there is usually a single non-daemon thread
2 növ mövzu var: demonlar və qeyri-daemonlar. Daemon mövzuları arxa planda bəzi işləri yerinə yetirən arxa plan mövzularıdır (xidmət mövzuları). Bu maraqlı termin “Maksvellin iblisi”nə istinaddır, bu barədə daha çox “
cinlər ” haqqında Vikipediya məqaləsində oxuya bilərsiniz. Sənədlərdə qeyd edildiyi kimi, JVM proqramı (prosesi) yerinə yetirməyə davam edir:
- Runtime.exit metodu çağırılmır
- Bütün qeyri-daemon mövzuları işlərini tamamladı (həm səhvsiz, həm də atılan istisnalarla)
Beləliklə, vacib detal: demon ipləri yerinə yetirilən istənilən əmrdə dayandırıla bilər. Buna görə də, onlarda məlumatların bütövlüyü təmin edilmir. Buna görə də, demon ipləri bəzi xidmət tapşırıqları üçün uyğundur. Məsələn, Java-da Zibil Kollektoru (GC) ilə əlaqəli son üsulların və ya mövzuların işlənməsi üçün cavabdeh olan bir ip var.
Hər mövzu hansısa qrupa ( ThreadGroup ) aiddir . Və qruplar bəzi iyerarxiya və ya struktur yaradaraq bir-birinə daxil ola bilər.
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());
}
Qruplar axınların idarə edilməsini sadələşdirməyə və onları izləməyə imkan verir. Qruplara əlavə olaraq mövzuların öz istisna işləyicisi var. Bir misala baxaq:
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);
}
Sıfıra bölmə işləyici tərəfindən tutulacaq bir xətaya səbəb olacaq. İşləyicini özünüz təyin etməsəniz, defolt işləyici tətbiqi işləyəcək və bu, StdError-da xəta yığınını göstərəcək.
Daha ətraflı http://pro-java.ru/java-dlya-opytnyx/obrabotchik-neperexvachennyx-isklyuchenij-java/ "" icmalında oxuya bilərsiniz . Bundan əlavə, mövzunun prioriteti var. Prioritetlər haqqında ətraflı oxuya bilərsiniz. məqalə "
Multithreading Java Thread Priority ".
Bir ipin yaradılması
Sənədlərdə deyildiyi kimi, ip yaratmaq üçün 2 yolumuz var. Birincisi, varisini yaratmaqdır. Misal üçün:
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();
}
}
Gördüyünüz kimi, tapşırıq metodda
run
, iplik isə metodda işə salınır
start
. Onları çaşdırmamaq lazımdır, çünki... metodu birbaşa işə salsaq
run
, heç bir yeni başlıq açılmayacaq.
start
JVM-dən yeni mövzu yaratmağı xahiş edən üsuldur . Thread-dan nəsli olan seçim pisdir, çünki biz Thread-ı sinif iyerarxiyasına daxil edirik. İkinci çatışmazlıq ondan ibarətdir ki, biz “Tək Məsuliyyət” SOLID prinsipini pozmağa başlayırıq, çünki sinifimiz eyni vaxtda həm mövzunun idarə edilməsinə, həm də bu mövzuda yerinə yetirilməli olan bəzi tapşırıqlara cavabdeh olur. Hansı düzgündür?
run
Cavab, ləğv etdiyimiz üsuldadır :
public void run() {
if (target != null) {
target.run();
}
}
Sinif nümunəsini yaradan zaman Thread-a keçə biləcəyimiz
target
bəziləri .
java.lang.Runnable
Buna görə də bunu edə bilərik:
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();
}
}
O , həmçinin
Runnable
Java 1.8-dən bəri funksional interfeysdir. Bu, mövzular üçün tapşırıq kodunu daha da gözəl yazmağa imkan verir:
public static void main(String []args){
Runnable task = () -> {
System.out.println("Hello, World!");
};
Thread thread = new Thread(task);
thread.start();
}
Ümumi
Beləliklə, ümid edirəm ki, bu hekayədən axın nədir, necə mövcuddur və onlarla hansı əsas əməliyyatları yerinə yetirmək olar.
Növbəti hissədə iplərin bir-biri ilə necə qarşılıqlı əlaqədə olduğunu və onların həyat dövrünün nə olduğunu başa düşməyə dəyər. #Viaçeslav
GO TO FULL VERSION