pengenalan
Jadi, kita tahu bahawa Java mempunyai benang. Anda boleh membaca tentang itu dalam ulasan bertajuk Better together: Java and the Thread class. Bahagian I — Benang pelaksanaan . Benang diperlukan untuk melaksanakan kerja secara selari. Ini menjadikannya sangat berkemungkinan bahawa benang akan berinteraksi antara satu sama lain. Mari kita lihat bagaimana ini berlaku dan apakah alat asas yang kita ada.
hasil
Thread.yield() membingungkan dan jarang digunakan. Ia diterangkan dalam pelbagai cara di Internet. Termasuk beberapa orang yang menulis bahawa terdapat beberapa baris gilir benang, di mana urutan akan turun berdasarkan keutamaan utas. Orang lain menulis bahawa thread akan menukar statusnya daripada "Running" kepada "Runnable" (walaupun tiada perbezaan antara status ini, iaitu Java tidak membezakan antara mereka). Realitinya adalah bahawa semuanya kurang terkenal tetapi lebih mudah dari segi erti kata.
yield()
dokumentasi kaedah. Jika anda membacanya, jelas bahawayield()
kaedah sebenarnya hanya memberikan beberapa cadangan kepada penjadual benang Java bahawa benang ini boleh diberi masa pelaksanaan yang lebih sedikit. Tetapi apa yang sebenarnya berlaku, iaitu sama ada penjadual bertindak mengikut pengesyoran dan perkara yang dilakukan secara umum, bergantung pada pelaksanaan JVM dan sistem pengendalian. Dan ia mungkin bergantung pada beberapa faktor lain juga. Semua kekeliruan berkemungkinan besar disebabkan oleh fakta bahawa multithreading telah difikirkan semula apabila bahasa Java telah berkembang. Baca lebih lanjut dalam gambaran keseluruhan di sini: Pengenalan Ringkas kepada Java Thread.yield() .
tidur
Seutas benang boleh tidur semasa pelaksanaannya. Ini adalah jenis interaksi yang paling mudah dengan utas lain. Sistem pengendalian yang menjalankan mesin maya Java yang menjalankan kod Java kami mempunyai penjadual benangnya sendiri . Ia menentukan utas mana untuk dimulakan dan bila. Pengaturcara tidak boleh berinteraksi dengan penjadual ini terus daripada kod Java, hanya melalui JVM. Dia boleh meminta penjadual untuk menjeda benang untuk seketika, iaitu untuk meletakkannya tidur. Anda boleh membaca lebih lanjut dalam artikel ini: Thread.sleep() dan Cara Multithreading berfungsi . Anda juga boleh menyemak cara utas berfungsi dalam sistem pengendalian Windows: Dalaman Windows Thread . Dan sekarang mari kita lihat dengan mata kepala kita sendiri. Simpan kod berikut dalam fail bernamaHelloWorldApp.java
:
class HelloWorldApp {
public static void main(String []args) {
Runnable task = () -> {
try {
int secToWait = 1000 * 60;
Thread.currentThread().sleep(secToWait);
System.out.println("Woke up");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(task);
thread.start();
}
}
Seperti yang anda lihat, kami mempunyai beberapa tugas yang menunggu selama 60 saat, selepas itu program tamat. Kami menyusun menggunakan arahan " javac HelloWorldApp.java
" dan kemudian menjalankan program menggunakan " java HelloWorldApp
". Adalah lebih baik untuk memulakan program dalam tetingkap yang berasingan. Sebagai contoh, pada Windows, ia adalah seperti ini: start java HelloWorldApp
. Kami menggunakan arahan jps untuk mendapatkan PID (ID proses), dan kami membuka senarai utas dengan " jvisualvm --openpid pid
: 
try {
TimeUnit.SECONDS.sleep(60);
System.out.println("Woke up");
} catch (InterruptedException e) {
e.printStackTrace();
}
Adakah anda perasan bahawa kami mengendalikan InterruptedException
di mana-mana? Mari kita fahami mengapa.
Thread.interrupt()
Masalahnya ialah semasa benang sedang menunggu/tidur, seseorang mungkin mahu mengganggu. Dalam kes ini, kami mengendalikanInterruptedException
. Mekanisme ini diwujudkan selepas Thread.stop()
kaedah itu diisytiharkan Dihentikan, iaitu ketinggalan zaman dan tidak diingini. Sebabnya ialah apabila stop()
kaedah itu dipanggil, benang itu hanya "dibunuh", yang sangat tidak dapat diramalkan. Kami tidak dapat mengetahui bila urutan akan dihentikan, dan kami tidak dapat menjamin konsistensi data. Bayangkan anda sedang menulis data ke fail semasa benang dimatikan. Daripada membunuh benang, pencipta Java memutuskan bahawa adalah lebih logik untuk memberitahunya bahawa ia harus diganggu. Cara untuk bertindak balas kepada maklumat ini adalah perkara yang harus diputuskan oleh benang itu sendiri. Untuk butiran lanjut, baca Mengapa Thread.stop ditamatkan?di laman web Oracle. Mari lihat contoh:
public static void main(String []args) {
Runnable task = () -> {
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
};
Thread thread = new Thread(task);
thread.start();
thread.interrupt();
}
Dalam contoh ini, kita tidak akan menunggu 60 saat. Sebaliknya, kami akan segera memaparkan "Terganggu". Ini kerana kami memanggil interrupt()
kaedah pada benang. Kaedah ini menetapkan bendera dalaman yang dipanggil "status gangguan". Iaitu, setiap utas mempunyai bendera dalaman yang tidak boleh diakses secara langsung. Tetapi kami mempunyai kaedah asli untuk berinteraksi dengan bendera ini. Tetapi itu bukan satu-satunya cara. Utas mungkin sedang berjalan, tidak menunggu sesuatu, hanya melakukan tindakan. Tetapi ia mungkin menjangkakan bahawa orang lain akan mahu menamatkan kerjanya pada masa tertentu. Sebagai contoh:
public static void main(String []args) {
Runnable task = () -> {
while(!Thread.currentThread().isInterrupted()) {
// Do some work
}
System.out.println("Finished");
};
Thread thread = new Thread(task);
thread.start();
thread.interrupt();
}
Dalam contoh di atas, while
gelung akan dilaksanakan sehingga benang diganggu secara luaran. Bagi isInterrupted
bendera, adalah penting untuk mengetahui bahawa jika kita menangkap InterruptedException
, bendera isInterrupted akan ditetapkan semula, dan kemudian isInterrupted()
akan kembali palsu. Kelas Thread juga mempunyai kaedah Thread.interrupted() statik yang digunakan hanya pada utas semasa, tetapi kaedah ini menetapkan semula bendera kepada false! Baca lebih lanjut dalam bab ini bertajuk Gangguan Benang .
Sertai (Tunggu thread lain selesai)
Jenis menunggu yang paling mudah ialah menunggu thread lain untuk selesai.
public static void main(String []args) throws InterruptedException {
Runnable task = () -> {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
};
Thread thread = new Thread(task);
thread.start();
thread.join();
System.out.println("Finished");
}
Dalam contoh ini, urutan baharu akan tidur 5 saat. Pada masa yang sama, benang utama akan menunggu sehingga benang tidur bangun dan menyelesaikan kerjanya. Jika anda melihat keadaan benang dalam JVisualVM, maka ia akan kelihatan seperti ini: 
join
agak mudah, kerana ia hanyalah kaedah dengan kod Java yang dilaksanakan wait()
selagi benang yang dipanggil masih hidup. Sebaik sahaja benang mati (apabila ia selesai dengan kerjanya), penantian terganggu. Dan itu semua keajaiban kaedah join()
. Jadi, mari kita beralih kepada perkara yang paling menarik.
Pantau
Multithreading merangkumi konsep monitor. Perkataan monitor datang ke bahasa Inggeris melalui bahasa Latin abad ke-16 dan bermaksud "alat atau peranti yang digunakan untuk memerhati, menyemak atau menyimpan rekod proses yang berterusan". Dalam konteks artikel ini, kami akan cuba membincangkan perkara asas. Bagi sesiapa yang mahukan butiran, sila selami bahan yang dipautkan. Kami memulakan perjalanan kami dengan Spesifikasi Bahasa Java (JLS): 17.1. Penyegerakan . Ia mengatakan perkara berikut:
lock()
atau melepaskannya dengan unlock()
. Seterusnya, kami akan menemui tutorial di laman web Oracle: Kunci Intrinsik dan Penyegerakan. Tutorial ini mengatakan bahawa penyegerakan Java dibina di sekeliling entiti dalaman yang dipanggil kunci intrinsik atau kunci monitor . Kunci ini selalunya dipanggil " monitor ". Kami juga melihat sekali lagi bahawa setiap objek di Jawa mempunyai kunci intrinsik yang dikaitkan dengannya. Anda boleh membaca Java - Kunci Intrinsik dan Penyegerakan . Seterusnya adalah penting untuk memahami bagaimana objek dalam Java boleh dikaitkan dengan monitor. Di Java, setiap objek mempunyai pengepala yang menyimpan metadata dalaman yang tidak tersedia kepada pengaturcara daripada kod, tetapi yang mesin maya perlu berfungsi dengan betul dengan objek. Pengepala objek termasuk "kata tanda", yang kelihatan seperti ini: 
https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf
public class HelloWorld{
public static void main(String []args){
Object object = new Object();
synchronized(object) {
System.out.println("Hello World");
}
}
}
Di sini, utas semasa (yang mana baris kod ini dilaksanakan) menggunakan synchronized
kata kunci untuk cuba menggunakan monitor yang dikaitkan denganobject"\
pembolehubah untuk mendapatkan/memperoleh kunci. Jika tiada orang lain yang bertanding untuk monitor (iaitu tiada orang lain yang menjalankan kod disegerakkan menggunakan objek yang sama), maka Java boleh cuba melakukan pengoptimuman yang dipanggil "penguncian berat sebelah". Teg yang berkaitan dan rekod tentang benang yang memiliki kunci monitor ditambahkan pada perkataan tanda dalam pengepala objek. Ini mengurangkan overhed yang diperlukan untuk mengunci monitor. Jika monitor sebelum ini dimiliki oleh benang lain, maka penguncian sedemikian tidak mencukupi. JVM bertukar kepada jenis penguncian seterusnya: "penguncian asas". Ia menggunakan operasi bandingkan-dan-tukar (CAS). Lebih-lebih lagi, kata tanda pengepala objek itu sendiri tidak lagi menyimpan perkataan tanda, sebaliknya merujuk kepada tempat ia disimpan, dan teg berubah supaya JVM memahami bahawa kami menggunakan penguncian asas. Jika berbilang benang bersaing (bertanding) untuk monitor (satu telah memperoleh kunci, dan satu saat sedang menunggu kunci dilepaskan), maka teg dalam perkataan tanda berubah, dan perkataan tanda kini menyimpan rujukan kepada monitor sebagai objek — beberapa entiti dalaman JVM. Seperti yang dinyatakan dalam JDK Enchancement Proposal (JEP), situasi ini memerlukan ruang dalam kawasan Native Heap memori untuk menyimpan entiti ini. Rujukan kepada lokasi memori entiti dalaman ini akan disimpan dalam kata tanda pengepala objek. Oleh itu, monitor adalah benar-benar mekanisme untuk menyegerakkan akses kepada sumber yang dikongsi antara berbilang benang. JVM bertukar antara beberapa pelaksanaan mekanisme ini. Jadi, untuk kesederhanaan, apabila bercakap tentang monitor, kita sebenarnya bercakap tentang kunci. dan satu saat sedang menunggu kunci dilepaskan), kemudian teg dalam perkataan tanda berubah, dan perkataan tanda kini menyimpan rujukan kepada monitor sebagai objek — beberapa entiti dalaman JVM. Seperti yang dinyatakan dalam JDK Enchancement Proposal (JEP), situasi ini memerlukan ruang dalam kawasan Native Heap memori untuk menyimpan entiti ini. Rujukan kepada lokasi memori entiti dalaman ini akan disimpan dalam kata tanda pengepala objek. Oleh itu, monitor adalah benar-benar mekanisme untuk menyegerakkan akses kepada sumber yang dikongsi antara berbilang benang. JVM bertukar antara beberapa pelaksanaan mekanisme ini. Jadi, untuk kesederhanaan, apabila bercakap tentang monitor, kita sebenarnya bercakap tentang kunci. dan satu saat sedang menunggu kunci dilepaskan), kemudian teg dalam perkataan tanda berubah, dan perkataan tanda kini menyimpan rujukan kepada monitor sebagai objek — beberapa entiti dalaman JVM. Seperti yang dinyatakan dalam JDK Enchancement Proposal (JEP), situasi ini memerlukan ruang dalam kawasan Native Heap memori untuk menyimpan entiti ini. Rujukan kepada lokasi memori entiti dalaman ini akan disimpan dalam kata tanda pengepala objek. Oleh itu, monitor adalah benar-benar mekanisme untuk menyegerakkan akses kepada sumber yang dikongsi antara berbilang benang. JVM bertukar antara beberapa pelaksanaan mekanisme ini. Jadi, untuk kesederhanaan, apabila bercakap tentang monitor, kita sebenarnya bercakap tentang kunci. dan perkataan tanda kini menyimpan rujukan kepada monitor sebagai objek — beberapa entiti dalaman JVM. Seperti yang dinyatakan dalam JDK Enchancement Proposal (JEP), situasi ini memerlukan ruang dalam kawasan Native Heap memori untuk menyimpan entiti ini. Rujukan kepada lokasi memori entiti dalaman ini akan disimpan dalam kata tanda pengepala objek. Oleh itu, monitor adalah benar-benar mekanisme untuk menyegerakkan akses kepada sumber yang dikongsi antara berbilang benang. JVM bertukar antara beberapa pelaksanaan mekanisme ini. Jadi, untuk kesederhanaan, apabila bercakap tentang monitor, kita sebenarnya bercakap tentang kunci. dan perkataan tanda kini menyimpan rujukan kepada monitor sebagai objek — beberapa entiti dalaman JVM. Seperti yang dinyatakan dalam JDK Enchancement Proposal (JEP), situasi ini memerlukan ruang dalam kawasan Native Heap memori untuk menyimpan entiti ini. Rujukan kepada lokasi memori entiti dalaman ini akan disimpan dalam kata tanda pengepala objek. Oleh itu, monitor adalah benar-benar mekanisme untuk menyegerakkan akses kepada sumber yang dikongsi antara berbilang benang. JVM bertukar antara beberapa pelaksanaan mekanisme ini. Jadi, untuk kesederhanaan, apabila bercakap tentang monitor, kita sebenarnya bercakap tentang kunci. Rujukan kepada lokasi memori entiti dalaman ini akan disimpan dalam kata tanda pengepala objek. Oleh itu, monitor adalah benar-benar mekanisme untuk menyegerakkan akses kepada sumber yang dikongsi antara berbilang benang. JVM bertukar antara beberapa pelaksanaan mekanisme ini. Jadi, untuk kesederhanaan, apabila bercakap tentang monitor, kita sebenarnya bercakap tentang kunci. Rujukan kepada lokasi memori entiti dalaman ini akan disimpan dalam kata tanda pengepala objek. Oleh itu, monitor adalah benar-benar mekanisme untuk menyegerakkan akses kepada sumber yang dikongsi antara berbilang benang. JVM bertukar antara beberapa pelaksanaan mekanisme ini. Jadi, untuk kesederhanaan, apabila bercakap tentang monitor, kita sebenarnya bercakap tentang kunci. 
Disegerakkan (menunggu kunci)
Seperti yang kita lihat sebelum ini, konsep "blok disegerakkan" (atau "bahagian kritikal") berkait rapat dengan konsep monitor. Lihat satu contoh:
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Runnable task = () -> {
synchronized(lock) {
System.out.println("thread");
}
};
Thread th1 = new Thread(task);
th1.start();
synchronized(lock) {
for (int i = 0; i < 8; i++) {
Thread.currentThread().sleep(1000);
System.out.print(" " + i);
}
System.out.println(" ...");
}
}
Di sini, utas utama mula-mula menghantar objek tugas ke utas baru, dan kemudian segera memperoleh kunci dan melakukan operasi yang panjang dengannya (8 saat). Selama ini, tugas itu tidak dapat diteruskan, kerana ia tidak boleh memasuki synchronized
blok, kerana kunci sudah diperoleh. Jika benang tidak mendapat kunci, ia akan menunggu monitor. Sebaik sahaja ia mendapat kunci, ia akan meneruskan pelaksanaan. Apabila benang keluar dari monitor, ia melepaskan kunci. Dalam JVisualVM, ia kelihatan seperti ini: 
th1.getState()
pernyataan dalam gelung for akan mengembalikan BLOCKED , kerana selagi gelung berjalan, lock
monitor objek diduduki oleh main
benang, dan th1
benang disekat dan tidak boleh diteruskan sehingga kunci dilepaskan. Selain blok disegerakkan, keseluruhan kaedah boleh disegerakkan. Sebagai contoh, berikut ialah kaedah daripada HashTable
kelas:
public synchronized int size() {
return count;
}
Kaedah ini akan dilaksanakan oleh hanya satu utas pada bila-bila masa. Adakah kita benar-benar memerlukan kunci? Ya, kami memerlukannya. Dalam kes kaedah contoh, objek "ini" (objek semasa) bertindak sebagai kunci. Terdapat perbincangan menarik mengenai topik ini di sini: Adakah terdapat kelebihan untuk menggunakan Kaedah Disegerakkan dan bukannya Blok Disegerakkan? . Jika kaedah statik, maka kunci tidak akan menjadi objek "ini" (kerana tiada objek "ini" untuk kaedah statik), sebaliknya objek Kelas (contohnya, ) Integer.class
.
Tunggu (menunggu monitor). kaedah notify() dan notifyAll().
Kelas Thread mempunyai kaedah menunggu lain yang dikaitkan dengan monitor. Tidak sepertisleep()
dan join()
, kaedah ini tidak boleh dipanggil begitu sahaja. Namanya ialah wait()
. Kaedah ini wait
dipanggil pada objek yang dikaitkan dengan monitor yang kita mahu tunggu. Mari lihat contoh:
public static void main(String []args) throws InterruptedException {
Object lock = new Object();
// The task object will wait until it is notified via lock
Runnable task = () -> {
synchronized(lock) {
try {
lock.wait();
} catch(InterruptedException e) {
System.out.println("interrupted");
}
}
// After we are notified, we will wait until we can acquire the lock
System.out.println("thread");
};
Thread taskThread = new Thread(task);
taskThread.start();
// We sleep. Then we acquire the lock, notify, and release the lock
Thread.currentThread().sleep(3000);
System.out.println("main");
synchronized(lock) {
lock.notify();
}
}
Dalam JVisualVM, ia kelihatan seperti ini: 
wait()
dan notify()
dikaitkan dengan java.lang.Object
. Ia mungkin kelihatan pelik bahawa kaedah berkaitan benang ada di dalam Object
kelas. Tetapi sebab untuk itu kini terbongkar. Anda akan ingat bahawa setiap objek dalam Java mempunyai pengepala. Pengepala mengandungi pelbagai maklumat pengemasan, termasuk maklumat mengenai monitor, iaitu status kunci. Ingat, setiap objek, atau contoh kelas, dikaitkan dengan entiti dalaman dalam JVM, dipanggil kunci atau monitor intrinsik. Dalam contoh di atas, kod untuk objek tugasan menunjukkan bahawa kita memasuki blok disegerakkan untuk monitor yang dikaitkan dengan lock
objek tersebut. Jika kita berjaya memperoleh kunci untuk monitor ini, makawait()
dipanggil. Benang yang melaksanakan tugas akan melepaskan lock
monitor objek, tetapi akan memasuki baris gilir benang menunggu pemberitahuan daripada lock
monitor objek. Barisan urutan ini dipanggil SET TUNGGU, yang lebih menggambarkan tujuannya dengan lebih tepat. Iaitu, ia lebih kepada satu set daripada baris gilir. Benang itu main
mencipta urutan baharu dengan objek tugasan, memulakannya dan menunggu 3 saat. Ini menjadikan besar kemungkinan bahawa utas baharu akan dapat memperoleh kunci sebelum utas main
dan masuk ke baris gilir monitor. Selepas itu, main
benang itu sendiri memasuki lock
blok disegerakkan objek dan melakukan pemberitahuan benang menggunakan monitor. Selepas pemberitahuan dihantar, main
benang mengeluarkanlock
monitor objek, dan utas baharu, yang sebelum ini menunggu lock
monitor objek dikeluarkan, meneruskan pelaksanaan. Adalah mungkin untuk menghantar pemberitahuan kepada hanya satu utas ( notify()
) atau serentak kepada semua utas dalam baris gilir ( notifyAll()
). Baca lebih lanjut di sini: Perbezaan antara notify() dan notifyAll() dalam Java . Adalah penting untuk ambil perhatian bahawa susunan pemberitahuan bergantung pada cara JVM dilaksanakan. Baca lebih lanjut di sini: Bagaimana untuk menyelesaikan kebuluran dengan notify dan notifyAll? . Penyegerakan boleh dilakukan tanpa menentukan objek. Anda boleh melakukan ini apabila keseluruhan kaedah disegerakkan dan bukannya satu blok kod. Sebagai contoh, untuk kaedah statik, kunci akan menjadi objek Kelas (diperolehi melalui .class
):
public static synchronized void printA() {
System.out.println("A");
}
public static void printB() {
synchronized(HelloWorld.class) {
System.out.println("B");
}
}
Dari segi penggunaan kunci, kedua-dua kaedah adalah sama. Jika kaedah tidak statik, maka penyegerakan akan dilakukan menggunakan arus instance
, iaitu menggunakan this
. By the way, kami katakan tadi anda boleh menggunakan getState()
kaedah untuk mendapatkan status thread. Sebagai contoh, untuk urutan dalam baris gilir menunggu monitor, status akan MENUNGGU atau TIMED_WAITING, jika kaedah wait()
menyatakan tamat masa. 
https://stackoverflow.com/questions/36425942/what-is-the-lifecycle-of-thread-in-java
Kitaran hayat benang
Sepanjang hayatnya, status benang berubah. Malah, perubahan ini merangkumi kitaran hayat benang. Sebaik sahaja urutan dibuat, statusnya adalah BARU. Dalam keadaan ini, benang baharu belum lagi berjalan dan penjadual benang Java belum mengetahui apa-apa tentangnya. Agar penjadual benang mengetahui tentang benang, anda mesti memanggilthread.start()
kaedah tersebut. Kemudian benang akan beralih ke keadaan RUNNABLE. Internet mempunyai banyak gambar rajah yang salah yang membezakan antara keadaan "Boleh Dijalankan" dan "Berjalan". Tetapi ini adalah kesilapan, kerana Java tidak membezakan antara "bersedia untuk bekerja" (boleh dijalankan) dan "bekerja" (berjalan). Apabila benang hidup tetapi tidak aktif (tidak Boleh Dijalankan), ia berada dalam salah satu daripada dua keadaan:
- DISEKAT — menunggu untuk memasuki bahagian kritikal, iaitu
synchronized
blok. - MENUNGGU — menunggu urutan lain untuk memenuhi beberapa syarat.
getState()
kaedah. Benang juga mempunyai isAlive()
kaedah, yang mengembalikan benar jika utas tidak DITAMATKAN.
LockSokongan dan letak benang
Bermula dengan Java 1.6, mekanisme menarik yang dipanggil LockSupport muncul.
park()
kaedah itu kembali serta-merta jika permit tersedia, menggunakan permit dalam proses. Jika tidak, ia menghalang. Memanggil unpark
kaedah menjadikan permit tersedia jika ia belum tersedia. Hanya ada 1 permit. Dokumentasi Java untuk LockSupport
merujuk kepada Semaphore
kelas. Mari lihat contoh mudah:
import java.util.concurrent.Semaphore;
public class HelloWorldApp{
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(0);
try {
semaphore.acquire();
} catch (InterruptedException e) {
// Request the permit and wait until we get it
e.printStackTrace();
}
System.out.println("Hello, World!");
}
}
Kod ini akan sentiasa menunggu, kerana sekarang semaphore mempunyai 0 permit. Dan apabila acquire()
dipanggil dalam kod (iaitu meminta permit), benang menunggu sehingga ia menerima permit. Memandangkan kita sedang menunggu, kita mesti mengendalikan InterruptedException
. Menariknya, semaphore mendapat keadaan benang yang berasingan. Jika kita melihat dalam JVisualVM, kita akan melihat bahawa negeri itu bukan "Tunggu", tetapi "Park". 
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
// Park the current thread
System.err.println("Will be Parked");
LockSupport.park();
// As soon as we are unparked, we will start to act
System.err.println("Unparked");
};
Thread th = new Thread(task);
th.start();
Thread.currentThread().sleep(2000);
System.err.println("Thread state: " + th.getState());
LockSupport.unpark(th);
Thread.currentThread().sleep(2000);
}
Status urutan akan MENUNGGU, tetapi JVisualVM membezakan antara wait
kata synchronized
kunci dan park
dari LockSupport
kelas. Mengapa ini LockSupport
sangat penting? Kami beralih semula ke dokumentasi Java dan melihat keadaan benang MENUNGGU . Seperti yang anda lihat, terdapat hanya tiga cara untuk masuk ke dalamnya. Dua daripada cara tersebut ialah wait()
dan join()
. Dan yang ketiga ialah LockSupport
. Di Java, kunci juga boleh dibina di atas LockSuppor
t dan menawarkan alat peringkat lebih tinggi. Jom cuba guna satu. Sebagai contoh, lihat ReentrantLock
:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{
public static void main(String []args) throws InterruptedException {
Lock lock = new ReentrantLock();
Runnable task = () -> {
lock.lock();
System.out.println("Thread");
lock.unlock();
};
lock.lock();
Thread th = new Thread(task);
th.start();
System.out.println("main");
Thread.currentThread().sleep(2000);
lock.unlock();
}
}
Sama seperti dalam contoh sebelumnya, semuanya mudah di sini. Objek lock
menunggu seseorang untuk mengeluarkan sumber yang dikongsi. Jika kita melihat dalam JVisualVM, kita akan melihat bahawa utas baharu akan diletakkan sehingga main
utas itu melepaskan kunci kepadanya. Anda boleh membaca lebih lanjut mengenai kunci di sini: Java 8 StampedLocks lwn. ReadWriteLocks dan API Disegerakkan dan Kunci dalam Java. Untuk lebih memahami cara kunci dilaksanakan, anda boleh membaca tentang Phaser dalam artikel ini: Panduan kepada Java Phaser . Dan bercakap tentang pelbagai penyegerak, anda mesti membaca artikel DZone tentang The Java Synchronizers.
Kesimpulan
Dalam ulasan ini, kami meneliti cara utama benang berinteraksi dalam Java. Bahan tambahan:- Lebih baik bersama: Java dan kelas Thread. Bahagian I - Benang pelaksanaan
- https://dzone.com/articles/the-java-synchronizers
- https://www.javatpoint.com/java-multithreading-interview-questions
GO TO FULL VERSION