pengenalan
Multithreading telah dibina ke dalam Java sejak zaman terawalnya. Jadi mari kita lihat sekilas tentang apa itu multithreading.
Mari 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]);
}
}
args
ialah 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
Kod 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) {
}
}
}
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:
Nombor 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
main
dipanggil titik masuk, atau "titik masuk". Kaedahnya
main
mestilah sentiasa
public static void
supaya 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 айдипроцесса
Menariknya, 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.
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
main
dan 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
run
secara langsung, tiada utas baru akan dimulakan. Ia adalah kaedah
start
yang 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
run
yang kami tolak:
public void run() {
if (target != null) {
target.run();
}
}
Berikut
target
ialah 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
Runnable
merupakan 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
GO TO FULL VERSION