Dalam bab ini, kami akan memperkenalkan metode waiting/bangun utas. Konten yang terlibat meliputi:
1. Pengantar untuk menunggu (), notify (), notifyall () dan metode lainnya
2. Tunggu () dan beri tahu ()
3. Tunggu (waktu lama) dan beri tahu ()
4. Tunggu () dan notifyall ()
5. Mengapa diberitahukan (), tunggu () dan fungsi lain yang ditentukan dalam objek, bukan utas
Pengantar untuk menunggu (), notify (), notifyall () dan metode lainnya
Di objek.java, antarmuka seperti tunggu (), notify () dan notifyAll () didefinisikan. Fungsi tunggu () adalah membiarkan utas saat ini memasukkan status menunggu, dan tunggu () juga akan membiarkan utas saat ini melepaskan kunci yang dipegangnya. Peran notify () dan notifyAll () adalah untuk membangunkan utas menunggu pada objek saat ini;
Detail API tentang menunggu/bangun di kelas objek adalah sebagai berikut:
Notify () - Bangun satu utas yang menunggu di monitor objek ini.
NotifyAll () - Bangun semua utas menunggu di monitor objek ini.
tunggu () - Masukkan utas saat ini dalam keadaan "tunggu (pemblokiran)" dan "sampai utas lain memanggil metode notify () atau metode notifyall () dari objek ini", dan utas saat ini dibangunkan (dimasukkan ke " negara siap ").
tunggu (jual waktu lama) - Biarkan utas saat ini berada dalam keadaan "tunggu (pemblokiran)" dan "sampai utas lain memanggil metode notify () atau metode notifyall () dari objek ini, atau melebihi jumlah waktu yang ditentukan", dan utas saat ini dibangunkan (masukkan "siap").
Tunggu (waktu lama, int nanos) - Masukkan utas saat ini dalam keadaan "tunggu (pemblokiran)" sampai utas lain memanggil metode notify () atau metode notifyall () dari objek ini, atau beberapa utas lainnya mengganggu utas saat ini, atau jumlah waktu yang sebenarnya telah melebihi ", dan utas saat ini dibangunkan (dimasukkan ke" negara siap ").
2. Tunggu () dan beri tahu () contoh
Berikut ini adalah contoh untuk menunjukkan "tunggu () dan beri tahu () bersama".
Salinan kode adalah sebagai berikut:
// Waittest.java Kode Sumber
class threada memperluas thread {
threada publik (nama string) {
super (nama);
}
public void run () {
disinkronkan (ini) {
System.out.println (thread.currentThread (). GetName ()+"call notify ()");
// bangunkan utas tunggu saat ini
memberitahu();
}
}
}
kelas publik waittest {
public static void main (string [] args) {
Threada t1 = threada baru ("t1");
disinkronkan (t1) {
mencoba {
// Mulai "Thread T1"
System.out.println (thread.currentThread (). GetName ()+"Mulai T1");
t1.start ();
// Utas utama menunggu T1 bangun melalui notify ().
System.out.println (thread.currentThread (). GetName ()+"tunggu ()");
t1.Wait ();
System.out.println (thread.currentThread (). GetName ()+"lanjutkan");
} catch (InterruptedException e) {
e.printstacktrace ();
}
}
}
}
Hasil Menjalankan:
Salinan kode adalah sebagai berikut:
Mulai utama T1
tunggu utama ()
T1 Call Notify ()
Terus berlanjut
Deskripsi Hasil:
Gambar berikut menggambarkan aliran "utas utama" dan "utas T1".
(01) Perhatikan bahwa "utas utama" pada gambar mewakili "utas utama utama". "Thread T1" mewakili "Thread T1" dimulai di Waittest. Dan "kunci" mewakili "kunci sinkron dari objek T1".
(02) "Thread Utama" membuat "Thread T1" baru melalui Threada baru ("T1"). Kemudian, "kunci sinkron dari objek T1" diperoleh melalui sinkronisasi (T1). Kemudian hubungi t1.start () untuk memulai "Thread T1".
(03) "Thread Utama" mengeksekusi t1.wait () untuk melepaskan "kunci objek T1" dan memasuki status "tunggu (pemblokiran)". Tunggu utas pada objek T1 untuk membangunkannya melalui notify () atau notifyall ().
(04) Setelah "Thread T1" dijalankan, "kunci objek saat ini" diperoleh melalui sinkronisasi (ini); lalu hubungi notify () untuk membangunkan "utas menunggu pada objek saat ini", yaitu, bangun "Utas utama".
(05) Setelah "Thread T1" selesai, lepaskan "kunci objek saat ini". Segera setelah itu, "utas utama" memperoleh "kunci objek T1" dan kemudian berjalan.
Untuk kode di atas? Seorang teman pernah bertanya: t1.Wait () harus membuat "thread t1" tunggu; tetapi mengapa "utas utama" tunggu?
Sebelum menjawab pertanyaan ini, mari kita lihat paragraf tentang menunggu di dokumen JDK:
Salinan kode adalah sebagai berikut:
Menyebabkan utas saat ini menunggu sampai utas lain memanggil metode notify () atau metode notifyall () untuk objek ini.
Dengan kata lain, metode ini berperilaku persis seolah -olah itu hanya melakukan panggilan tunggu (0).
Utas saat ini harus memiliki monitor objek ini. dapat menghambat kepemilikan monitor dan melanjutkan eksekusi.
Artinya dalam bahasa Cina adalah secara kasar:
Menyebabkan "utas saat ini" menunggu sampai utas lain memanggil notify () atau notifyall () untuk membangunkan utas. Dengan kata lain, metode ini memiliki efek yang sama dengan Wait (0)! (Tambahan, untuk metode Wait (Long Millis), ketika Millis adalah 0, itu berarti Tunggu Tanpa Tunggu sampai dibangunkan oleh Notify () atau NotifyAll ()).
Ketika panggilan "utas saat ini" tunggu (), ia harus memiliki kunci sinkronisasi untuk objek. Setelah panggilan utas tunggu (), kunci akan dilepaskan; maka itu akan menunggu sampai "utas lain" memanggil metode kunci sinkron objek () atau metode notifyall (). Utas kemudian terus menunggu sampai ia melakukan reaksi ulang "kunci sinkronisasi untuk objek ini" dan kemudian dapat terus berjalan.
Catatan: Dalam penjelasan JDK, dikatakan bahwa fungsi tunggu () adalah untuk membuat "utas saat ini" tunggu, dan "utas saat ini" mengacu pada utas yang berjalan pada CPU!
Ini juga berarti bahwa meskipun t1.wait () adalah metode tunggu () yang dipanggil melalui "Thread T1", tempat di mana t1.wait () dipanggil berada di "utas utama utama". Utas utama haruslah "utas saat ini", yaitu keadaan berjalan, sebelum t1.wait () dapat dieksekusi. Oleh karena itu, "utas saat ini" saat ini adalah "utas utama utama"! Oleh karena itu, t1.wait () adalah untuk membuat "utas utama" tunggu, bukan "utas T1"!
3. Tunggu (waktu lama) dan beri tahu ()
Tunggu (waktu lama) akan menempatkan utas saat ini dalam keadaan "tunggu (pemblokiran)" dan "sampai utas lain memanggil metode notify () atau metode notifyall () dari objek ini, atau melebihi jumlah waktu yang ditentukan", dan Benang saat ini dibangunkan (dimasukkan) "siap").
Contoh berikut menunjukkan batas waktu menunggu (waktu lama), dan utasnya terbangun.
Salinan kode adalah sebagai berikut:
// Kode Sumber WaitTimeOutTest.java
class threada memperluas thread {
threada publik (nama string) {
super (nama);
}
public void run () {
System.out.println (thread.currentThread (). GetName () + "run");
// Siklus setan, berjalan terus menerus.
Sementara (benar)
}
}
kelas publik WaitTimeOutTest {
public static void main (string [] args) {
Threada t1 = threada baru ("t1");
disinkronkan (t1) {
mencoba {
// Mulai "Thread T1"
System.out.println (thread.currentThread (). GetName () + "Mulai T1");
t1.start ();
// Utas utama menunggu T1 bangun melalui notify () atau notifyall (), atau penundaan melebihi 3000ms;
System.out.println (thread.currentThread (). GetName () + "call wait");
t1.wait (3000);
System.out.println (thread.currentThread (). GetName () + "lanjutkan");
} catch (InterruptedException e) {
e.printstacktrace ();
}
}
}
}
Hasil Menjalankan:
Salinan kode adalah sebagai berikut:
Mulai utama T1
Tunggu Panggilan Utama
T1 Run // Setelah sekitar 3 detik ... output "Main Lanjutkan"
Terus berlanjut
Deskripsi Hasil:
Gambar berikut menggambarkan aliran "utas utama" dan "utas T1".
(01) Perhatikan bahwa "utas utama" pada gambar mewakili utas utama WaitTimeOuttest (mis., Utas utama). "Thread T1" mewakili Thread T1 dimulai di Waittest. Dan "kunci" mewakili "kunci sinkron dari objek T1".
(02) Utas utama utama mengeksekusi T1.start () untuk memulai "Thread T1".
(03) Utas utama utama mengeksekusi T1.Wait (3000), dan pada saat ini, utas utama memasuki "status pemblokiran". Perlu untuk "utas yang digunakan untuk kunci objek T1 untuk membangunkannya melalui notify () atau notifyall ()" atau "setelah batas waktu 3000ms", utas utama memasuki "keadaan siap" dan kemudian dapat berjalan.
(04) Setelah "Thread T1" berjalan, ia memasuki loop mati dan terus berjalan.
(05) Setelah timeout adalah 3000ms, utas utama akan memasuki "status siap" dan kemudian memasuki "status berjalan".
4. Tunggu () dan notifyall ()
Melalui contoh sebelumnya, kita tahu bahwa notify () dapat membangunkan satu utas yang menunggu pada monitor objek ini.
Di bawah ini, kami menunjukkan penggunaan NotifyAll () melalui contoh;
Salinan kode adalah sebagai berikut:
kelas publik notifyalltest {
objek statis privat obj = objek baru ();
public static void main (string [] args) {
Threada t1 = threada baru ("t1");
Threada t2 = threada baru ("t2");
Threada t3 = threada baru ("t3");
t1.start ();
t2.start ();
t3.start ();
mencoba {
System.out.println (thread.currentThread (). GetName ()+"sleep (3000)");
Thread.sleep (3000);
} catch (InterruptedException e) {
e.printstacktrace ();
}
disinkronkan (obj) {
// Utas utama sedang menunggu.
System.out.println (thread.currentThread (). GetName ()+"notifyAll ()");
obj.notifyall ();
}
}
kelas statis threada memperluas thread {
threada publik (nama string) {
super (nama);
}
public void run () {
disinkronkan (obj) {
mencoba {
// Cetak hasil output
System.out.println (thread.currentThread (). GetName () + "tunggu");
// bangunkan utas tunggu saat ini
obj.wait ();
// Cetak hasil output
System.out.println (thread.currentThread (). GetName () + "lanjutkan");
} catch (InterruptedException e) {
e.printstacktrace ();
}
}
}
}
}
Hasil Menjalankan:
Salinan kode adalah sebagai berikut:
T1 tunggu
Tidur Utama (3000)
T3 tunggu
T2 tunggu
NotifyAll utama ()
T2 Lanjutkan
T3 Lanjutkan
T1 Lanjutkan
Deskripsi Hasil:
Lihat diagram alur di bawah ini.
(01) 3 Thread "T1", "T2" dan "T3" dibuat dan dimulai di utas utama.
(02) Benang utama tidur selama 3 detik melalui tidur (3000). Selama utas utama tidur selama 3 detik, kami berasumsi bahwa tiga utas "T1", "T2" dan "T3" semuanya berjalan. Ambil "T1" sebagai contoh. juga akan menunggu utas lain untuk membangunkannya melalui nofity () atau nofityAll ().
(03) Benang utama tidur selama 3 detik dan kemudian berjalan. Jalankan obj.notifyall () untuk membangunkan utas menunggu di OBJ, yaitu, bangunkan tiga utas "t1", "t2" dan "t3". Segera setelah utas utama disinkronkan (OBJ) dijalankan, utas utama melepaskan "OBJ Lock". Dengan cara ini, "T1", "T2" dan "T3" dapat memperoleh "Obj Lock" dan terus berjalan!
5. Mengapa diberitahukan (), tunggu () dan fungsi lain yang ditentukan dalam objek, bukan utas
Fungsi seperti tunggu (), notify () di objek, seperti disinkronkan, akan beroperasi pada "kunci sinkronisasi objek".
tunggu () akan membuat "utas saat ini" tunggu. tidak bisa berlari!
Oke, setelah panggilan utas tunggu (), itu akan melepaskan "kunci sinkron" yang dipegang oleh kuncinya; dan, menurut pengantar sebelumnya, kita tahu bahwa utas penantian dapat dibangunkan oleh notify () atau notifyall (). Sekarang, tolong pikirkan tentang pertanyaan: apa yang diberitahukan () berdasarkan kebangkitan utas menunggu? Atau, apa korelasi antara tunggu () dan notify ()? Jawabannya adalah: berdasarkan "kunci sinkronisasi objek".
Utas yang bertanggung jawab untuk membangunkan utas menunggu (kami menyebutnya "Bangun utas"), itu hanya memperoleh "kunci sinkronisasi objek" (kunci sinkronisasi di sini harus sama dengan kunci sinkronisasi utas menunggu), dan panggilan notify () atau setelah notifyall () metode, utas menunggu dapat dibangunkan. Meskipun, utas menunggu dibangunkan; Anda harus menunggu sampai utas bangun merilis "kunci sinkronisasi objek" sebelum Anda dapat memperoleh "kunci sinkronisasi objek" dan terus berjalan.
Singkatnya, beri tahu (), tunggu () bergantung pada "kunci sinkron", yang dipegang oleh kunci objek, dan setiap objek memiliki dan hanya satu! Inilah sebabnya mengapa fungsi seperti notify (), wait () didefinisikan di kelas objek, bukan di kelas utas.