CodeGym /Java Blogu /Rastgele /Birlikte daha iyi: Java ve Thread sınıfı. Bölüm I - Yürüt...
John Squirrels
Seviye
San Francisco

Birlikte daha iyi: Java ve Thread sınıfı. Bölüm I - Yürütme Konuları

grupta yayınlandı

giriiş

Çoklu iş parçacığı en başından beri Java'da yerleşiktir. Öyleyse, multithreading denen şeye kısaca bir göz atalım. Birlikte daha iyi: Java ve Thread sınıfı.  Bölüm I - Yürütme Konuları - 1Oracle'ın resmi dersini referans noktası olarak alıyoruz: " Ders: "Merhaba Dünya!" Uygulaması ". Merhaba Dünya programımızın kodunu aşağıdaki gibi biraz değiştireceğiz:

class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsprogram başlatıldığında iletilen girdi parametreleri dizisidir. Bu kodu, sınıf adıyla eşleşen ve .java. Javac yardımcı programını kullanarak derleyin : javac HelloWorldApp.java. Ardından, bazı parametrelerle kodumuzu çalıştırıyoruz, örneğin, "Anlaşıldı": java HelloWorldApp Roger Birlikte daha iyi: Java ve Thread sınıfı.  Bölüm I - Yürütme Konuları - 2Kodumuzda şu anda ciddi bir kusur var. Herhangi bir argüman iletmezseniz (yani sadece "java HelloWorldApp" yürütün), o zaman bir hata alırız:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
"Main" adlı iş parçacığında bir istisna (yani bir hata) oluştu. Yani, Java'nın konuları var mı? Yolculuğumuzun başladığı yer burasıdır.

Java ve iş parçacıkları

Bir iş parçacığının ne olduğunu anlamak için bir Java programının nasıl başladığını anlamanız gerekir. Kodumuzu aşağıdaki gibi değiştirelim:

class HelloWorldApp {
    public static void main(String[] args) {
		while (true) { 
			// Do nothing
		}
	}
}
Şimdi tekrar ile derleyelim javac. Kolaylık sağlamak için Java kodumuzu ayrı bir pencerede çalıştıracağız. Windows'ta bu şu şekilde yapılabilir: start java HelloWorldApp. Şimdi Java'nın bize hangi bilgileri söyleyebileceğini görmek için jps yardımcı programını kullanacağız : Birlikte daha iyi: Java ve Thread sınıfı.  Bölüm I - Yürütme Konuları - 3İlk sayı PID veya İşlem Kimliğidir. süreç nedir?

A process is a combination of code and data sharing a common virtual address space.
Süreçlerle, farklı programlar çalışırken birbirinden izole edilir: her uygulama, diğer programlara müdahale etmeden bellekte kendi alanını kullanır. Daha fazla bilgi edinmek için şu öğreticiyi okumanızı tavsiye ederim: İşlemler ve Konular . Bir işlem, iş parçacığı olmadan var olamaz, bu nedenle bir işlem varsa, en az bir iş parçacığı vardır. Peki bu Java'da nasıl oluyor? Bir Java programını başlattığımızda, yürütme yöntemiyle başlar main. Sanki programa adım atıyoruz, bu yüzden bu özel mainyönteme giriş noktası denir. mainJava sanal makinesinin (JVM) programımızı yürütmeye başlayabilmesi için yöntem her zaman "public static void" olmalıdır . Daha fazla bilgi için, Java ana yöntemi neden statiktir?. Java başlatıcısının (java.exe veya javaw.exe) basit bir C uygulaması olduğu ortaya çıktı: aslında JVM'yi oluşturan çeşitli DLL'leri yükler. Java başlatıcısı, belirli bir dizi Java Yerel Arayüz (JNI) çağrısı yapar. JNI, Java sanal makinesinin dünyasını C++ dünyasıyla bağlamak için bir mekanizmadır. Bu nedenle, başlatıcı JVM'nin kendisi değil, onu yüklemek için bir mekanizmadır. JVM'yi başlatmak için yürütülecek doğru komutları bilir. Gerekli ortamı kurmak için JNI çağrılarını nasıl kullanacağını bilir. Bu ortamın kurulması, elbette "main" olarak adlandırılan ana iş parçacığının oluşturulmasını içerir. Bir Java işleminde hangi iş parçacıklarının bulunduğunu daha iyi göstermek için jvisualvm'yi kullanırız.JDK ile birlikte gelen araç. Bir işlemin pid'ini bildiğimizde, o işlemle ilgili bilgileri hemen görebiliriz: jvisualvm --openpid <process id> Birlikte daha iyi: Java ve Thread sınıfı.  Bölüm I - Yürütme Konuları - 4İlginç bir şekilde, her iş parçacığının, işleme ayrılan bellekte kendi ayrı alanı vardır. Bu bellek yapısına yığın denir. Bir yığın çerçevelerden oluşur. Çerçeve, bir yöntemin etkinleştirilmesini temsil eder (bitmemiş bir yöntem çağrısı). Çerçeve, StackTraceElement olarak da temsil edilebilir (bkz. StackTraceElement için Java API ). Buradaki tartışmada her iş parçacığına ayrılan bellek hakkında daha fazla bilgi bulabilirsiniz: " Java (JVM) her iş parçacığı için yığını nasıl ayırır ". Java API'sine bakıp "Thread" kelimesini ararsanız, java.lang.Thread'i bulacaksınız.sınıf. Bu, Java'da bir iş parçacığını temsil eden sınıftır ve onunla çalışmamız gerekecek. Birlikte daha iyi: Java ve Thread sınıfı.  Bölüm I - Yürütme Konuları - 5

java.lang.Thread

Java'da bir iş parçacığı, sınıfın bir örneği tarafından temsil edilir java.lang.Thread. Hemen anlamalısınız ki, Thread sınıfı örnekleri kendilerinin yürütme evreleri değildir. Bu, JVM ve işletim sistemi tarafından yönetilen düşük seviyeli iş parçacıkları için bir tür API'dir. Java başlatıcısını kullanarak JVM'yi başlattığımızda, main"main" adlı bir iş parçacığı ve birkaç başka temizlik iş parçacığı oluşturur. JavaDoc'ta Thread sınıfı için belirtildiği gibi: When a Java Virtual Machine starts up, there is usually a single non-daemon thread. 2 tür iş parçacığı vardır: daemonlar ve daemon olmayanlar. Daemon iş parçacıkları, arka planda bazı işler yapan arka plan (temizlik) iş parçacıklarıdır. "Daemon" kelimesi Maxwell'in iblisine atıfta bulunur. Bu Wikipedia makalesinde daha fazla bilgi edinebilirsiniz . Dokümantasyonda belirtildiği gibi, JVM programı (işlemi) yürütmeye devam eder:
  • Runtime.exit () yöntemi çağrılır
  • Arka plan programı OLMAYAN tüm iş parçacıkları işlerini bitirir (hatasız veya atılan istisnalarla)
Buradan önemli bir detay çıkar: daemon thread'ler herhangi bir noktada sonlandırılabilir. Sonuç olarak, verilerinin bütünlüğü hakkında hiçbir garanti yoktur. Buna göre, daemon iş parçacıkları belirli temizlik görevleri için uygundur. Örneğin, Java'nın yöntem çağrılarını işlemekten sorumlu bir iş parçacığı vardır finalize(), yani Çöp Toplayıcı (gc) ile ilgili iş parçacıkları. Her iş parçacığı bir grubun ( ThreadGroup ) parçasıdır . Ve gruplar, belirli bir hiyerarşi veya yapı oluşturarak diğer grupların parçası olabilir.

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());
}
Gruplar, iş parçacığı yönetimine düzen getirir. Gruplara ek olarak, iş parçacıklarının kendi istisna işleyicileri vardır. Bir örneğe göz atın:

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ölme, işleyici tarafından yakalanacak bir hataya neden olur. Kendi işleyicinizi belirtmezseniz, JVM, istisnanın yığın izlemesini StdError'a çıkaran varsayılan işleyiciyi çağırır. Her iş parçacığının da bir önceliği vardır. Bu makalede öncelikler hakkında daha fazla bilgi edinebilirsiniz: Multithreading'de Java İş Parçacığı Önceliği .

Konu oluşturma

Belgelerde belirtildiği gibi, bir iş parçacığı oluşturmak için 2 yolumuz var. İlk yol, kendi alt sınıfınızı yaratmaktır. Örneğin:

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üğünüz gibi, görevin çalışması yöntemde gerçekleşir run(), ancak iş parçacığının kendisi yöntemde başlatılır start(). Bu metotları karıştırmayın: r metodunu direk çağırırsak un()yeni thread başlamaz. start()JVM'den yeni bir iş parçacığı oluşturmasını isteyen yöntemdir . Thread'i devraldığımız bu seçenek zaten kötü, çünkü Thread'i sınıf hiyerarşimize dahil ediyoruz. İkinci dezavantaj, "tek sorumluluk" ilkesini ihlal etmeye başlamamızdır. Yani, sınıfımız eş zamanlı olarak iş parçacığının kontrolünden ve bu iş parçacığında gerçekleştirilecek bazı görevlerden sorumludur. Doğru yol nedir? run()Yanıt , geçersiz kıldığımız aynı yöntemde bulunur :

public void run() {
	if (target != null) {
		target.run();
	}
}
İşte, Thread sınıfının bir örneğini oluştururken geçebileceğimiz targetbazı . java.lang.RunnableBu, şunu yapabileceğimiz anlamına gelir:

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();
    }
}
Runnableayrıca Java 1.8'den beri işlevsel bir arayüz olmuştur. Bu, bir iş parçacığının görevi için daha da güzel kod yazmayı mümkün kılar:

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

Çözüm

Umarım bu tartışma bir iş parçacığının ne olduğunu, iş parçacıklarının nasıl ortaya çıktığını ve iş parçacıklarıyla hangi temel işlemlerin gerçekleştirilebileceğini açıklığa kavuşturmuştur. Bir sonraki bölümde , iş parçacıklarının birbirleriyle nasıl etkileşime girdiğini anlamaya ve iş parçacığı yaşam döngüsünü keşfetmeye çalışacağız. Birlikte daha iyi: Java ve Thread sınıfı. Bölüm II — Eşitleme Birlikte daha iyi: Java ve Thread sınıfı. Bölüm III — Etkileşim Birlikte Daha İyi: Java ve Thread sınıfı. Bölüm IV — Callable, Future ve arkadaşlar Birlikte daha iyi: Java ve Thread sınıfı. Bölüm V — Yürütücü, ThreadPool, Fork/Join Birlikte daha iyi: Java ve Thread sınıfı. Bölüm VI - Ateş edin!
Yorumlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION