JavaRush /Blog Java /Random-MS /Anda Tidak Boleh Merosakkan Java dengan Benang: Bahagian ...

Anda Tidak Boleh Merosakkan Java dengan Benang: Bahagian I - Benang

Diterbitkan dalam kumpulan

pengenalan

Multithreading telah dibina ke dalam Java sejak zaman terawalnya. Jadi mari kita lihat sekilas tentang apa itu multithreading. Anda tidak boleh merosakkan Java dengan Thread: Bahagian I - Threads - 1Mari kita ambil pelajaran rasmi dari Oracle sebagai titik permulaan: " Pelajaran: Aplikasi "Hello World!" ". Mari tukar kod aplikasi Hello World kami sedikit kepada yang berikut:
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsialah tatasusunan parameter input yang diluluskan apabila program bermula. Mari simpan kod ini pada fail dengan nama yang sepadan dengan nama kelas dan sambungan .java. Mari kita susun menggunakan utiliti javac : javac HelloWorldApp.java Selepas itu, hubungi kod kami dengan beberapa parameter, contohnya, Roger: java HelloWorldApp Roger Anda tidak boleh merosakkan Java dengan Thread: Bahagian I - Threads - 2Kod kami kini mempunyai kecacatan yang serius. Jika kami tidak meluluskan sebarang hujah (iaitu hanya jalankan java HelloWorldApp), kami akan mendapat ralat:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
Pengecualian (iaitu ralat) berlaku dalam urutan bernama main. Ternyata terdapat beberapa jenis benang di Jawa? Di sinilah bermulanya perjalanan kami.

Java dan benang

Untuk memahami apa itu benang, anda perlu memahami cara aplikasi Java dilancarkan. Mari tukar kod kami seperti berikut:
class HelloWorldApp {
    public static void main(String[] args) {
		while (true) {
			//Do nothing
		}
	}
}
Sekarang mari kita susun semula menggunakan javac. Seterusnya, untuk kemudahan, kami akan menjalankan kod Java kami dalam tetingkap berasingan. Pada Windows anda boleh melakukan ini seperti ini: start java HelloWorldApp. Sekarang, menggunakan utiliti jps , mari lihat maklumat yang Java akan beritahu kami: Anda tidak boleh merosakkan Java dengan Thread: Bahagian I - Threads - 3Nombor pertama ialah PID atau ID Proses, pengecam proses. Apakah proses?
Процесс — это совокупность codeа и данных, разделяющих общее виртуальное addressное пространство.
Dengan bantuan proses, pelaksanaan program yang berbeza diasingkan antara satu sama lain: setiap aplikasi menggunakan kawasan memorinya sendiri tanpa mengganggu program lain. Saya menasihati anda untuk membaca artikel dengan lebih terperinci: " https://habr.com/post/164487/ ". Proses tidak boleh wujud tanpa utas, jadi jika proses wujud, sekurang-kurangnya satu utas wujud di dalamnya. Bagaimanakah ini berlaku di Jawa? Apabila kita menjalankan program Java, pelaksanaannya bermula dengan main. Kami jenis memasuki program, jadi kaedah khas ini maindipanggil titik masuk, atau "titik masuk". Kaedahnya mainmestilah sentiasa public static voidsupaya Mesin Maya Java (JVM) boleh mula melaksanakan program kami. Lihat " Mengapa kaedah utama Java statik? " untuk butiran lanjut. Ternyata pelancar java (java.exe atau javaw.exe) ialah aplikasi mudah (aplikasi C mudah): ia memuatkan pelbagai DLL, yang sebenarnya adalah JVM. Pelancar Java membuat set panggilan Java Native Interface (JNI) tertentu. JNI ialah mekanisme yang menghubungkan dunia Mesin Maya Java dan dunia C++. Ternyata pelancar itu bukan JVM, tetapi pemuatnya. Ia mengetahui arahan yang betul untuk dilaksanakan untuk memulakan JVM. Tahu cara mengatur semua persekitaran yang diperlukan menggunakan panggilan JNI. Organisasi persekitaran ini juga termasuk penciptaan utas utama, yang biasanya dipanggil main. Untuk melihat dengan lebih jelas benang yang hidup dalam proses java, kami menggunakan program jvisualvm , yang disertakan dalam JDK. Mengetahui pid proses, kami boleh membuka data padanya dengan segera: jvisualvm --openpid айдипроцесса Anda tidak boleh merosakkan Java dengan Thread: Bahagian I - Threads - 4Menariknya, setiap thread mempunyai kawasan tersendiri dalam memori yang diperuntukkan untuk proses tersebut. Struktur ingatan ini dipanggil timbunan. Timbunan terdiri daripada bingkai. Bingkai ialah titik memanggil kaedah, titik pelaksanaan. Bingkai juga boleh diwakili sebagai StackTraceElement (lihat Java API untuk StackTraceElement ). Anda boleh membaca lebih lanjut tentang memori yang diperuntukkan kepada setiap utas di sini . Jika kita melihat Java API dan mencari perkataan Thread, kita akan melihat bahawa terdapat kelas java.lang.Thread . Kelas inilah yang mewakili aliran di Jawa, dan dengan ini kita perlu bekerja. Thread'ом Java не испортишь: Часть I — потоки - 5

java.lang.Thread

Benang dalam Java diwakili sebagai contoh kelas java.lang.Thread. Perlu difahami dengan segera bahawa contoh kelas Thread di Jawa bukanlah utas itu sendiri. Ini hanyalah sejenis API untuk urutan peringkat rendah yang diuruskan oleh JVM dan sistem pengendalian. Apabila kami melancarkan JVM menggunakan pelancar java, ia mencipta utas utama dengan nama maindan beberapa lagi utas perkhidmatan. Seperti yang dinyatakan dalam kelas JavaDoc bagi Thread: When a Java Virtual Machine starts up, there is usually a single non-daemon thread Terdapat 2 jenis utas: daemon dan bukan daemon. Benang Daemon ialah benang latar belakang (benang perkhidmatan) yang melakukan beberapa kerja di latar belakang. Istilah menarik ini merujuk kepada "setan Maxwell," yang boleh anda baca lebih lanjut dalam artikel Wikipedia tentang " setan ." Seperti yang dinyatakan dalam dokumentasi, JVM terus melaksanakan program (proses) sehingga:
  • Kaedah Runtime.exit tidak dipanggil
  • Semua utas bukan daemon menyelesaikan kerja mereka (kedua-duanya tanpa ralat dan dengan pengecualian yang dilemparkan)
Oleh itu perincian penting: benang daemon boleh ditamatkan pada mana-mana arahan yang dilaksanakan. Oleh itu, integriti data di dalamnya tidak dijamin. Oleh itu, benang daemon sesuai untuk beberapa tugas perkhidmatan. Sebagai contoh, di Jawa terdapat benang yang bertanggungjawab untuk memproses kaedah akhir atau benang yang berkaitan dengan Pengumpul Sampah (GC). Setiap thread tergolong dalam beberapa kumpulan ( ThreadGroup ). Dan kumpulan boleh masuk ke dalam satu sama lain, membentuk beberapa hierarki atau struktur.
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());
}
Kumpulan membolehkan anda menyelaraskan pengurusan aliran dan menjejakinya. Selain kumpulan, benang mempunyai pengendali pengecualiannya sendiri. Mari lihat contoh:
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);
}
Pembahagian dengan sifar akan menyebabkan ralat yang akan ditangkap oleh pengendali. Jika anda tidak menentukan sendiri pengendali, pelaksanaan pengendali lalai akan berfungsi, yang akan memaparkan timbunan ralat dalam StdError. Anda boleh membaca lebih lanjut dalam ulasan http://pro-java.ru/java-dlya-opytnyx/obrabotchik-neperexvachennyx-isklyuchenij-java/ ". Di samping itu, benang mempunyai keutamaan. Anda boleh membaca lebih lanjut mengenai keutamaan dalam artikel " Java Thread Priority dalam Multithreading ".

Mencipta benang

Seperti yang dinyatakan dalam dokumentasi, kami mempunyai 2 cara untuk mencipta benang. Yang pertama ialah mencipta waris anda. Sebagai contoh:
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();
    }
}
Seperti yang anda lihat, tugasan dilancarkan dalam kaedah run, dan utas dilancarkan dalam kaedah start. Mereka tidak boleh keliru, kerana... jika kita menjalankan kaedah runsecara langsung, tiada utas baru akan dimulakan. Ia adalah kaedah startyang meminta JVM membuat utas baharu. Pilihan dengan keturunan daripada Thread adalah buruk kerana kami memasukkan Thread dalam hierarki kelas. Kelemahan kedua ialah kita mula melanggar prinsip "Tanggungjawab Tunggal" PADAT, kerana kelas kami menjadi bertanggungjawab serentak untuk mengurus benang dan untuk beberapa tugas yang mesti dilakukan dalam urutan ini. Mana yang betul? Jawapannya adalah dalam kaedah runyang kami tolak:
public void run() {
	if (target != null) {
		target.run();
	}
}
Berikut targetialah beberapa java.lang.Runnable, yang boleh kita hantar ke Thread semasa membuat contoh kelas. Oleh itu, kita boleh melakukan ini:
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();
    }
}
Ia juga Runnablemerupakan antara muka berfungsi sejak Java 1.8. Ini membolehkan anda menulis kod tugas untuk benang dengan lebih cantik:
public static void main(String []args){
	Runnable task = () -> {
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

Jumlah

Jadi, saya harap daripada cerita ini jelas apa itu aliran, bagaimana ia wujud dan apakah operasi asas yang boleh dilakukan dengannya. Dalam bahagian seterusnya , anda perlu memahami cara utas berinteraksi antara satu sama lain dan apakah kitaran hidup mereka. #Viacheslav
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION