JavaRush /Java Blog /Random-ID /Anda tidak dapat merusak Java dengan utas: Bagian II - si...
Viacheslav
Level 3

Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi

Dipublikasikan di grup Random-ID

Perkenalan

Jadi, kita tahu bahwa ada thread di Java, yang bisa Anda baca di ulasan “ Anda Tidak Dapat Memanjakan Java dengan Thread: Bagian I - Threads ”. Utas diperlukan untuk melakukan pekerjaan secara bersamaan. Oleh karena itu, kemungkinan besar thread tersebut akan berinteraksi satu sama lain. Mari kita pahami bagaimana hal ini terjadi dan kontrol dasar apa yang kita miliki. Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 1

Menghasilkan

Metode Thread.yield() misterius dan jarang digunakan. Ada banyak variasi deskripsinya di Internet. Sampai-sampai ada yang menulis tentang semacam antrian thread, di mana thread tersebut akan bergerak ke bawah dengan mempertimbangkan prioritasnya. Seseorang menulis bahwa thread akan mengubah statusnya dari running menjadi runnable (walaupun tidak ada pembagian ke dalam status ini, dan Java tidak membedakannya). Namun kenyataannya, segala sesuatunya jauh lebih tidak diketahui dan, dalam arti tertentu, lebih sederhana. Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 2Pada topik dokumentasi metode, yieldada bug " JDK-6416721: (spec thread) Fix Thread.yield() javadoc ". Jika Anda membacanya, jelas bahwa sebenarnya metode tersebut yieldhanya menyampaikan beberapa rekomendasi kepada penjadwal thread Java agar thread ini dapat diberikan waktu eksekusi yang lebih sedikit. Namun apa yang sebenarnya akan terjadi, apakah penjadwal akan mendengarkan rekomendasi tersebut dan apa yang akan dilakukannya secara umum bergantung pada implementasi JVM dan sistem operasi. Atau mungkin dari beberapa faktor lainnya. Semua kebingungan ini kemungkinan besar disebabkan oleh pemikiran ulang multithreading selama pengembangan bahasa Java. Anda dapat membaca lebih lanjut di review " Pengantar Singkat Java Thread.yield() ".

Tidur - Benang tertidur

Sebuah thread mungkin tertidur selama eksekusinya. Ini adalah jenis interaksi paling sederhana dengan thread lain. Sistem operasi tempat mesin virtual Java diinstal, tempat kode Java dijalankan, memiliki penjadwal thread sendiri, yang disebut Penjadwal Thread. Dialah yang memutuskan thread mana yang akan dijalankan, kapan. Pemrogram tidak dapat berinteraksi dengan penjadwal ini secara langsung dari kode Java, namun ia dapat, melalui JVM, meminta penjadwal untuk menjeda thread untuk sementara waktu, untuk "mengalihkannya ke mode tidur". Anda dapat membaca lebih lanjut di artikel " Thread.sleep() " dan " Cara kerja Multithreading ". Selain itu, Anda dapat mengetahui cara kerja thread di OS Windows: " Internals of Windows Thread ". Sekarang kita akan melihatnya dengan mata kepala kita sendiri. Mari simpan kode berikut ke file HelloWorldApp.java:
class HelloWorldApp {
    public static void main(String []args) {
        Runnable task = () -> {
            try {
                int secToWait = 1000 * 60;
                Thread.currentThread().sleep(secToWait);
                System.out.println("Waked up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
Seperti yang Anda lihat, kami memiliki tugas yang menunggu 60 detik, setelah itu program berakhir. Kami mengkompilasi javac HelloWorldApp.javadan menjalankan java HelloWorldApp. Lebih baik meluncurkannya di jendela terpisah. Misalnya di Windows akan seperti ini: start java HelloWorldApp. Dengan menggunakan perintah jps, kami mengetahui PID proses dan membuka daftar thread menggunakan jvisualvm --openpid pidПроцесса: Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 3Seperti yang Anda lihat, thread kami telah memasuki status Tidur. Faktanya, tidur utas saat ini dapat dilakukan dengan lebih indah:
try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Waked up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
Anda mungkin memperhatikan bahwa kami memproses di mana saja InterruptedException? Mari kita pahami alasannya.

Mengganggu thread atau Thread.interrupt

Masalahnya adalah ketika thread sedang menunggu dalam keadaan tidur, seseorang mungkin ingin menghentikan penantian ini. Dalam hal ini, kami menangani pengecualian tersebut. Ini dilakukan setelah metode ini Thread.stopdinyatakan Tidak Digunakan Lagi, mis. ketinggalan jaman dan tidak diinginkan untuk digunakan. Alasannya adalah ketika metode ini dipanggil, stopthread tersebut hanya “dimatikan”, yang sangat tidak dapat diprediksi. Kami tidak tahu kapan aliran ini akan dihentikan, kami tidak bisa menjamin konsistensi datanya. Bayangkan Anda menulis data ke file dan kemudian alirannya dimusnahkan. Oleh karena itu, mereka memutuskan bahwa akan lebih logis untuk tidak menghentikan aliran tersebut, tetapi untuk memberitahukannya bahwa aliran tersebut harus dihentikan. Bagaimana menyikapi hal ini terserah pada aliran itu sendiri. Detail selengkapnya dapat ditemukan di " Mengapa Thread.stop tidak digunakan lagi? " Oracle Mari kita lihat sebuah 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();
}
Pada contoh ini, kita tidak akan menunggu 60 detik, namun akan langsung mencetak 'Interrupted'. Ini karena kami memanggil metode thread interrupt. Metode ini menetapkan "bendera internal yang disebut status interupsi". Artinya, setiap thread memiliki flag internal yang tidak dapat diakses secara langsung. Namun kami memiliki metode asli untuk berinteraksi dengan tanda ini. Tapi ini bukan satu-satunya cara. Sebuah thread dapat sedang dalam proses eksekusi, tidak menunggu sesuatu, namun hanya melakukan tindakan. Namun hal ini dapat menyatakan bahwa mereka ingin menyelesaikannya pada titik tertentu dalam pekerjaannya. Misalnya:
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();
}
Pada contoh di atas, Anda dapat melihat bahwa loop whileakan berjalan hingga thread diinterupsi secara eksternal. Hal penting yang perlu diketahui tentang flag isInterrupted adalah jika kita menangkapnya InterruptedException, flag tersebut isInterruptedakan disetel ulang, dan kemudian isInterruptedakan menghasilkan false. Ada juga metode statis di kelas Thread yang hanya berlaku untuk thread saat ini - Thread.interrupted() , tetapi metode ini menyetel ulang tanda ke false! Anda dapat membaca lebih lanjut di bab " Interupsi Thread ".

Gabung — Menunggu thread lain selesai

Jenis penantian yang paling sederhana adalah menunggu thread lain 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, thread baru akan tidur selama 5 detik. Pada saat yang sama, thread utama akan menunggu hingga thread yang tertidur bangun dan menyelesaikan pekerjaannya. Jika Anda melihat melalui JVisualVM, status thread akan terlihat seperti ini: Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 4Berkat alat pemantauan, Anda dapat melihat apa yang terjadi dengan thread. Caranya joincukup sederhana, karena ini hanyalah sebuah metode dengan kode java yang dijalankan waitsaat thread yang dipanggil masih hidup. Setelah thread mati (saat terminasi), penantian dihentikan. Itulah keajaiban metode ini join. Oleh karena itu, mari kita beralih ke bagian yang paling menarik.

Pemantau Konsep

Dalam multithreading ada yang namanya Monitor. Secara umum, kata monitor diterjemahkan dari bahasa Latin sebagai “pengawas” atau “pengawas”. Dalam kerangka artikel ini, kami akan mencoba mengingat esensinya, dan bagi yang mau, saya minta Anda mendalami materi dari tautan untuk detailnya. Mari kita mulai perjalanan kita dengan spesifikasi bahasa Java yaitu dengan JLS: " 17.1. Sinkronisasi ". Dikatakan sebagai berikut: Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 5Ternyata untuk tujuan sinkronisasi antar thread, Java menggunakan mekanisme tertentu yang disebut “Monitor”. Setiap objek memiliki monitor yang terkait dengannya, dan thread dapat mengunci atau membuka kuncinya. Selanjutnya, kita akan menemukan tutorial pelatihan di situs Oracle: “ Kunci Intrinsik dan Sinkronisasi ”. Tutorial ini menjelaskan bahwa sinkronisasi di Java dibangun di sekitar entitas internal yang dikenal sebagai kunci intrinsik atau kunci monitor. Seringkali kunci seperti itu disebut "monitor". Kita juga melihat lagi bahwa setiap objek di Java mempunyai kunci intrinsik yang terkait dengannya. Anda dapat membaca " Java - Kunci dan Sinkronisasi Intrinsik ". Selanjutnya, penting untuk memahami bagaimana suatu objek di Java dapat dikaitkan dengan monitor. Setiap objek di Java memiliki header - semacam metadata internal yang tidak dapat diakses oleh pemrogram dari kode, tetapi diperlukan mesin virtual agar dapat bekerja dengan objek dengan benar. Header objek menyertakan MarkWord yang terlihat seperti ini: Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 6

https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf

Sebuah artikel dari Habr sangat berguna di sini: " Tapi bagaimana cara kerja multithreading? Bagian I: sinkronisasi ." Pada artikel ini ada baiknya menambahkan deskripsi dari Ringkasan blok tugas dari bugtaker JDK: “ JDK-8183909 ”. Anda dapat membaca hal yang sama di " JEP-8183909 ". Jadi, di Java, monitor dikaitkan dengan suatu objek dan thread dapat memblokir thread ini, atau mereka juga mengatakan "dapatkan kunci". Contoh paling sederhana:
public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
Jadi, dengan menggunakan kata kunci, synchronizedthread saat ini (di mana baris kode ini dijalankan) mencoba menggunakan monitor yang terkait dengan objek tersebut objectdan "mendapatkan kunci" atau "menangkap monitor" (opsi kedua bahkan lebih disukai). Jika tidak ada perselisihan untuk monitor (yaitu tidak ada orang lain yang ingin melakukan sinkronisasi pada objek yang sama), Java dapat mencoba melakukan optimasi yang disebut "penguncian bias". Judul objek di Mark Word akan berisi tag yang sesuai dan catatan thread mana yang dilampirkan monitor. Hal ini mengurangi overhead saat menangkap monitor. Jika monitor telah diikat ke thread lain sebelumnya, maka penguncian ini tidak cukup. JVM beralih ke tipe penguncian berikutnya - penguncian dasar. Ini menggunakan operasi perbandingan dan pertukaran (CAS). Pada saat yang sama, header di Mark Word tidak lagi menyimpan Mark Word itu sendiri, tetapi tautan ke penyimpanannya + tag diubah sehingga JVM memahami bahwa kita menggunakan penguncian dasar. Jika ada pertikaian untuk monitor dari beberapa thread (satu telah menangkap monitor, dan yang kedua sedang menunggu monitor dilepaskan), maka tag di Mark Word berubah, dan Mark Word mulai menyimpan referensi ke monitor sebagai sebuah objek - beberapa entitas internal JVM. Sebagaimana dinyatakan dalam JEP, dalam hal ini, diperlukan ruang di area memori Native Heap untuk menyimpan entitas ini. Tautan ke lokasi penyimpanan entitas internal ini akan ditempatkan di objek Mark Word. Jadi, seperti yang bisa kita lihat, monitor sebenarnya merupakan mekanisme untuk memastikan sinkronisasi akses beberapa thread ke sumber daya bersama. Ada beberapa implementasi mekanisme ini yang digunakan oleh JVM. Oleh karena itu, untuk mempermudah, ketika berbicara tentang monitor, sebenarnya kita berbicara tentang kunci. Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 7

Disinkronkan dan menunggu dengan kunci

Konsep monitor, seperti yang telah kita lihat sebelumnya, berkaitan erat dengan konsep “blok sinkronisasi” (atau disebut juga bagian kritis). Mari kita lihat sebuah 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, thread utama pertama-tama mengirimkan tugas ke thread baru, dan kemudian segera “menangkap” kunci dan melakukan operasi yang lama dengannya (8 detik). Selama ini tugas tidak bisa masuk blok untuk pelaksanaannya synchronized, karena kuncinya sudah terisi. Jika thread tidak dapat memperoleh kunci, maka thread tersebut akan menunggu di monitor. Begitu diterima, eksekusi akan dilanjutkan. Ketika seutas benang meninggalkan monitor, kuncinya akan terlepas. Di JVisualVM akan terlihat seperti ini: Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 8Seperti yang Anda lihat, status di JVisualVM disebut "Monitor" karena thread diblokir dan tidak dapat menempati monitor. Anda juga dapat mengetahui status utas dalam kode, tetapi nama status ini tidak sesuai dengan istilah JVisualVM, meskipun serupa. Dalam hal ini, th1.getState()loop forakan mengembalikan BLOCKED , karena Saat loop berjalan, monitor lockditempati mainoleh thread, dan thread th1diblokir dan tidak dapat terus bekerja sampai kunci dikembalikan. Selain blok sinkronisasi, seluruh metode dapat disinkronkan. Misalnya, metode dari kelas HashTable:
public synchronized int size() {
	return count;
}
Dalam satu satuan waktu, metode ini hanya akan dieksekusi oleh satu thread. Tapi kita butuh kunci, kan? Ya, saya membutuhkannya. Dalam kasus metode objek, kuncinya adalah this. Ada diskusi menarik mengenai topik ini: " Apakah ada keuntungan menggunakan Metode Tersinkronisasi daripada Blok Tersinkronisasi? ". Jika metodenya statis, maka kuncinya bukan this(karena untuk metode statis tidak boleh this), tetapi objek kelasnya (Misalnya, Integer.class).

Tunggu dan tunggu di monitor. Metode notify dan notifyAll

Thread memiliki metode tunggu lain, yang terhubung ke monitor. Berbeda dengan sleepdan join, itu tidak bisa dipanggil begitu saja. Dan namanya adalah wait. Metode ini dijalankan waitpada objek yang monitornya ingin kita tunggu. Mari kita lihat contohnya:
public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // task будет ждать, пока его не оповестят через lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // После оповещения нас мы будем ждать, пока сможем взять лок
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // Ждём и после этого забираем себе лок, оповещаем и отдаём лок
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
Di JVisualVM akan terlihat seperti ini: Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 10Untuk memahami cara kerjanya, Anda harus ingat bahwa metode waitmengacu notifypada java.lang.Object. Tampaknya aneh bahwa metode terkait thread ada di file Object. Namun di sinilah letak jawabannya. Seperti yang kita ingat, setiap objek di Java memiliki header. Header berisi berbagai informasi layanan, termasuk informasi tentang monitor—data tentang status penguncian. Dan seperti yang kita ingat, setiap objek (yaitu setiap instance) memiliki asosiasi dengan entitas internal JVM yang disebut kunci intrinsik, yang juga disebut monitor. Pada contoh di atas, tugasnya menjelaskan bahwa kita memasukkan blok sinkronisasi pada monitor yang terkait dengan lock. Jika memungkinkan untuk mendapatkan kunci pada monitor ini, maka wait. Thread yang menjalankan tugas ini akan melepaskan monitor lock, namun akan bergabung dengan antrian thread yang menunggu pemberitahuan di monitor lock. Antrian utas ini disebut WAIT-SET, yang lebih mencerminkan esensinya. Ini lebih merupakan satu set daripada antrian. Utas mainmembuat utas baru dengan tugas tugas, memulainya dan menunggu selama 3 detik. Hal ini memungkinkan, dengan tingkat kemungkinan yang tinggi, thread baru mengambil kunci sebelum thread maindan mengantri di monitor. Setelah itu thread mainitu sendiri memasuki blok sinkronisasi lockdan melakukan notifikasi thread di monitor. Setelah notifikasi terkirim, thread mainmelepaskan monitor lock, dan thread baru (yang sebelumnya menunggu) lockmelanjutkan eksekusi setelah menunggu monitor dilepaskan. Dimungkinkan untuk mengirim pemberitahuan hanya ke salah satu thread ( notify) atau ke semua thread dalam antrian sekaligus ( notifyAll). Anda dapat membaca lebih lanjut di " Perbedaan antara notify() dan notifyAll() di Java ". Penting untuk dicatat bahwa urutan notifikasi bergantung pada implementasi JVM. Anda dapat membaca lebih lanjut di " Bagaimana cara mengatasi kelaparan dengan notify dan notifyall? ". Sinkronisasi dapat dilakukan tanpa menentukan objek. Ini dapat dilakukan ketika bukan bagian kode yang terpisah yang disinkronkan, tetapi seluruh metode. Misalnya, untuk metode statis, kuncinya akan menjadi objek kelas (diperoleh melalui .class):
public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
Dalam hal penggunaan kunci, kedua metode tersebut sama. Jika metodenya tidak statis, maka sinkronisasi akan dilakukan sesuai dengan arus instance, yaitu menurut this. Ngomong-ngomong, sebelumnya kami mengatakan bahwa dengan menggunakan metode ini getStateAnda bisa mendapatkan status utas. Jadi disini adalah thread yang di antri oleh monitor, statusnya adalah WAITING atau TIMED_WAITING jika metode waitmenentukan batas waktu tunggu. Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 11

Siklus hidup sebuah thread

Seperti yang telah kita lihat, arus mengubah statusnya seiring berjalannya kehidupan. Intinya, perubahan ini adalah siklus hidup thread. Saat thread baru saja dibuat, statusnya BARU. Dalam posisi ini, belum dimulai dan Java Thread Scheduler belum mengetahui apa pun tentang thread baru. Agar penjadwal thread mengetahui tentang thread, Anda harus menghubungi thread.start(). Kemudian thread akan masuk ke status RUNNABLE. Ada banyak skema yang salah di Internet yang memisahkan status Runnable dan Running. Tapi ini adalah sebuah kesalahan, karena... Java tidak membedakan status "siap dijalankan" dan "berjalan". Ketika sebuah thread masih hidup namun tidak aktif (bukan Runnable), thread tersebut berada dalam salah satu dari dua keadaan:
  • DIBLOKIR - menunggu masuk ke bagian yang dilindungi, mis. ke synchonizedblok.
  • WAITING - menunggu thread lain berdasarkan kondisi. Jika kondisinya benar, penjadwal thread akan memulai thread.
Jika thread menunggu waktu, maka thread tersebut berstatus TIMED_WAITING. Jika thread tidak lagi berjalan (berhasil diselesaikan atau dengan pengecualian), maka thread akan masuk ke status TERMINASI. Untuk mengetahui keadaan suatu thread (keadaannya) digunakan metode getState. Thread juga memiliki metode isAliveyang mengembalikan nilai true jika thread tidak Dihentikan.

LockSupport dan parkir thread

Sejak Java 1.6 ada mekanisme menarik yang disebut LockSupport . Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 12Kelas ini mengaitkan "izin" atau izin dengan setiap thread yang menggunakannya. Pemanggilan metode parksegera kembali jika izin tersedia, menggunakan izin yang sama selama panggilan. Kalau tidak, itu akan diblokir. Memanggil metode ini unparkmembuat izin tersedia jika belum tersedia. Hanya ada 1 Izin. Di Java API, LockSupportizin tertentu Semaphore. Mari kita lihat contoh sederhana:
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) {
            // Просим разрешение и ждём, пока не получим его
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
Kode ini akan menunggu selamanya karena semaphore sekarang memiliki 0 izin. Dan ketika dipanggil dalam kode acquire(yaitu, meminta izin), thread menunggu hingga menerima izin. Karena kami menunggu, kami wajib memprosesnya InterruptedException. Menariknya, semaphore mengimplementasikan status thread terpisah. Jika kita melihat di JVisualVM, kita akan melihat bahwa keadaan kita bukan Wait, tapi Park. Anda tidak dapat merusak Java dengan utas: Bagian II - sinkronisasi - 13Mari kita lihat contoh lainnya:
public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            //Запаркуем текущий поток
            System.err.println("Will be Parked");
            LockSupport.park();
            // Как только нас распаркуют - начнём действовать
            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 threadnya adalah MENUNGGU, tetapi JVisualVM membedakan waitantara from synchronizeddan parkfrom LockSupport. Mengapa hal ini begitu penting LockSupport? Mari kita kembali ke Java API dan melihat Thread State WAITING . Seperti yang Anda lihat, hanya ada tiga cara untuk masuk ke dalamnya. 2 cara - ini waitdan join. Dan yang ketiga adalah LockSupport. Kunci di Java dibangun berdasarkan prinsip yang sama LockSupportdan mewakili alat tingkat yang lebih tinggi. Mari kita coba menggunakannya. Mari kita lihat, misalnya, pada 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();
    }
}
Seperti pada contoh sebelumnya, semuanya sederhana di sini. lockmenunggu seseorang melepaskan sumber daya. Jika kita melihat di JVisualVM, kita akan melihat bahwa thread baru akan diparkir sampai mainthread tersebut memberikan kuncinya. Anda dapat membaca lebih lanjut tentang kunci di sini: " Pemrograman multithread di Java 8. Bagian dua. Menyinkronkan akses ke objek yang dapat diubah " dan " Java Lock API. Teori dan contoh penggunaan ." Untuk lebih memahami penerapan kunci, ada gunanya membaca tentang Phazer di ikhtisar " Kelas Phaser ". Dan berbicara tentang berbagai sinkronisasi, Anda harus membaca artikel di Habré “ Java.util.concurrent.* Referensi Sinkronisasi ”.

Total

Dalam ulasan ini, kami melihat cara utama thread berinteraksi di Java. Material tambahan: #Viacheslav
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION