Kata kunci disinkronkan
Kunci yang disinkronkan dapat memodifikasi fungsi dan pernyataan dalam fungsi. Apakah itu ditambahkan ke metode atau objek, kunci yang diperolehnya adalah objek, daripada memperlakukan sepotong kode atau fungsi sebagai kunci.
1. Ketika dua utas bersamaan mengakses blok kode yang disinkronkan (ini) di objek yang sama, hanya satu utas yang dapat dieksekusi untuk jangka waktu tertentu, dan utas lainnya hanya dapat menjalankan kode ini setelah utas saat ini telah menyelesaikan eksekusi.
2. Ketika utas mengakses blok kode yang disinkronkan (ini) dalam suatu objek, utas lain masih dapat mengakses blok kode non-sinkron (ini) dalam objek ini.
3. Perlu dicatat di sini bahwa ketika utas mengakses blok kode yang disinkronkan (ini) dari suatu objek, utas lain akan diblokir dari mengakses blok kode yang disinkronkan (ini) yang disinkronkan dalam objek ini.
4. Di atas juga berlaku untuk blok kode sinkronisasi lainnya, yaitu, ketika utas mengakses blok kode sinkronisasi yang disinkronkan (ini) dari suatu objek, utas memperoleh kunci objek objek. Selain itu, setiap objek (mis., Contoh kelas) sesuai dengan kunci. miliknya diblokir. Setelah metode dieksekusi, kunci akan ditempati secara eksklusif sampai dirilis ketika kembali dari metode dan masuk kembali ke negara bagian yang dapat dieksekusi. Mekanisme ini memastikan bahwa pada saat yang sama, untuk setiap objek, paling banyak dari semua fungsi anggota yang dinyatakan disinkronkan berada dalam keadaan yang dapat dieksekusi (karena paling banyak satu utas dapat memperoleh kunci objek), sehingga menghindari akses ke konflik variabel anggota kelas kelas konflik kelas kelas kelas kelas kelas kelas kelas kelas kelas) .
Kerugian metode yang disinkronkan:
Karena tersinkronisasi mengunci objek yang menyebut metode sinkronisasi ini, yaitu, ketika utas P1 menjalankan metode ini di utas yang berbeda, mereka akan membentuk pengecualian timbal balik, sehingga mencapai efek sinkronisasi. Tetapi harus dicatat di sini bahwa objek lain dari kelas yang merupakan objek ini dapat secara sewenang -wenang menyebut metode ini dengan kata kunci yang disinkronkan ditambahkan. Esensi dari metode sinkronisasi adalah menerapkan sinkronisasi pada referensi objek. Kasus ini. Kami akan menjelaskan situasi ini secara rinci di bawah ini:
Pertama, mari kita perkenalkan dua objek yang terkunci dengan kata kunci yang disinkronkan: objek dan kelas - disinkronkan dapat menambahkan kunci objek atau kunci kelas ke sumber daya. Dari kelas ini, objek lain dari kelas ini masih dapat menggunakan metode yang disinkronkan yang telah mengunci objek sebelumnya.
Salah satu masalah utama yang kita diskusikan di sini adalah: "Akankah kelas yang sama dan contoh yang berbeda menyebut metode yang sama, akankah ada masalah sinkronisasi?"
Masalah sinkronisasi hanya terkait dengan sumber daya, dan itu tergantung pada apakah sumber daya statis. Untuk data statis yang sama, fungsi Anda yang sama milik utas yang berbeda untuk membaca dan menulisnya pada saat yang sama, dan CPU tidak akan menghasilkan kesalahan. Anda. Bahkan jika Anda memiliki dua kode berbeda yang berjalan dalam dua core yang berbeda dari CPU dan menulis alamat memori secara bersamaan, mekanisme cache akan mengunci satu di L2 terlebih dahulu. Kemudian perbarui dan bagikan dengan inti lain, dan tidak akan ada kesalahan, jika tidak Intel atau AMD akan sia -sia.
Oleh karena itu, selama Anda tidak memiliki sumber daya atau variabel yang sama dengan dua berbagi kode, tidak akan ada inkonsistensi data. Selain itu, panggilan ke objek yang berbeda dari kelas yang sama memiliki tumpukan yang sama sekali berbeda, dan mereka sama sekali tidak relevan.
Di sini kami menggunakan contoh untuk menggambarkan proses penjualan tiket, di mana sumber daya bersama kami adalah sisa tiket.
Paket com.test; Public ThreadSafetest memperluas implementasi utas Runnable {private static int num = 1; void jual (name string) {if (num> 0) {out.println (name + ": jumlah suara deteksi lebih besar dari 0"); (selesai dalam waktu sekitar 5 detik). " ; "Sistem: Jumlah suara saat ini:" + num); args []) {coba {new ThreadSafetest ("Penjual tiket Li xx") .start (); {E.PrintStackTrace ();}}} Jalankan kode di atas dan output yang kami dapatkan adalah:
Konduktor Tiket Li XX: Jumlah tiket uji lebih besar dari 0 Konduktor Tiket LI XX: Pembayaran sedang dikumpulkan (selesai dalam waktu sekitar 5 detik). . . Penjual Tiket King X: Jumlah tiket uji lebih besar dari 0 Penjual Tiket King X: Pembayaran sedang dikumpulkan (selesai dalam waktu sekitar 5 detik). . . Penjual Tiket Li XX: Cetak tagihan, Sistem Penyelesaian Penjualan Tiket: Jumlah Suara Saat Ini: 0 Penjual Tiket Wang X: Cetak tagihan, Sistem Penyelesaian Penjualan Tiket: Jumlah suara saat ini: -1 PERINGATAN: Jumlah suara lebih rendah dari 0, angka negatif muncul
Berdasarkan hasil output, kita dapat menemukan bahwa suara yang tersisa adalah -1, dan ada masalah kesalahan sinkronisasi. Alasan untuk ini adalah bahwa dua objek instance yang kami buat telah memodifikasi sumber statis statis statis bersama num = 1 secara bersamaan. Kemudian kami menghapus statis pengubah di dalam kotak di dalam kode di atas, dan kemudian jalankan program untuk mendapatkan:
Konduktor Tiket Li XX: Jumlah tiket uji lebih besar dari 0 Konduktor Tiket LI XX: Pembayaran sedang dikumpulkan (selesai dalam waktu sekitar 5 detik). . . Penjual Tiket King X: Jumlah tiket uji lebih besar dari 0 Penjual Tiket King X: Pembayaran sedang dikumpulkan (selesai dalam waktu sekitar 5 detik). . . Penjual Tiket Li XX: Cetak tagihan, Sistem Penyelesaian Penjualan Tiket: Jumlah tiket saat ini: 0 Penjual tiket Wang X: Cetak tagihan, Sistem Penyelesaian Penjualan Tiket: Jumlah tiket saat ini: 0
Setelah memodifikasi gelar, program berjalan tanpa masalah. Setiap objek memiliki tumpukan dan berjalan sendiri secara mandiri. Tetapi ini bertentangan dengan harapan kami bahwa banyak utas dapat menangani sumber daya bersama pada saat yang sama (setelah statis, num berubah dari sumber daya bersama ke variabel anggota yang dimiliki oleh setiap contoh), yang jelas bukan yang kami inginkan.
Dalam dua kode di atas, hal utama yang harus diadopsi adalah mengunci objek. Untuk alasan yang saya sebutkan sebelumnya, ketika dua contoh kelas yang berbeda memodifikasi sumber daya yang sama, CPU akan default ke logika program. Oleh karena itu, kita perlu mengubah ruang lingkup kunci. pada saat yang sama.
Paket com.test; Public ThreadSafetest memperluas implementasi utas Runnable {private static int num = 1; static void jual (nama string) {if (num> 0) {System. mengumpulkan pembayaran (sekitar 5 detik selesai). " (}} Tangkap (interupsi E) {e.printstacktrace (); .println ("Sistem: Hitungan Voting Saat Ini:" + Num); String args []) {coba {new ThreadSafetest ("Penjual tiket Li xx") .Start (); ) {E.PrintStackTrace ();}}} Buat program seperti di atas untuk mendapatkan hasil yang dijalankan:
Konduktor Tiket Li XX: Jumlah tiket uji lebih besar dari 0 Konduktor Tiket LI XX: Pembayaran sedang dikumpulkan (selesai dalam waktu sekitar 5 detik). . . Penjual Tiket Li XX: Cetak Tiket, Sistem Penyelesaian Penjualan Tiket: Jumlah Tiket Saat Ini: 0 Penjual Tiket Wang X: Tidak Ada Tiket, Hentikan Penjualan Tiket
Pengubah statis ditambahkan ke metode sell (), sehingga objek kunci menjadi kelas. Ini akan mendapatkan hasil yang kami inginkan seperti yang diharapkan.
Meringkaskan:
1. Ada dua penggunaan kata kunci yang disinkronkan: metode yang disinkronkan dan blok yang disinkronkan.
2. Di Java, ini bukan hanya contoh kelas, tetapi setiap kelas juga dapat sesuai dengan kunci.
1. Kata kunci yang disinkronkan tidak dapat diwarisi. Meskipun disinkronkan dapat digunakan untuk mendefinisikan metode, disinkronkan bukan milik bagian dari definisi metode, sehingga kata kunci yang disinkronkan tidak dapat diwariskan. Jika metode di kelas induk menggunakan kata kunci yang disinkronkan dan subkelas juga mengesampingkan metode ini, secara default, metode ini dalam subkelas tidak disinkronkan, dan harus ditampilkan untuk menambahkan metode dalam subkelas. Hanya kata kunci yang disinkronkan yang dapat digunakan . Tentu saja, Anda juga dapat memanggil metode yang sesuai di kelas induk di subkelas. disinkronkan. menyukai,
Tambahkan kata kunci yang disinkronkan ke subkelas:
class Parent {public Synchronized void Method () {}} class Child memperluas Parent {Metode void disinkronkan publik () {}} Hubungi metode kelas induk:
class Parent {public Synchronized void Method () {}} class Child Extends Parent {public void Metode () {super.method (); 2. Kata kunci yang disinkronkan tidak dapat digunakan saat mendefinisikan metode antarmuka.
3. Konstruktor tidak dapat menggunakan kata kunci yang disinkronkan, tetapi blok yang disinkronkan dapat digunakan untuk sinkronisasi.
4. Posisi yang disinkronkan dapat ditempatkan secara bebas, tetapi tidak dapat ditempatkan di belakang jenis pengembalian metode.
5. Kata kunci yang disinkronkan tidak dapat digunakan untuk menyinkronkan variabel, seperti kode berikut salah:
Int N = 0 yang disinkronkan publik;
6. Meskipun menggunakan kata kunci yang disinkronkan adalah metode sinkronisasi paling aman, jika digunakan dalam jumlah besar, itu juga akan menyebabkan konsumsi sumber daya yang tidak perlu dan kerugian kinerja. Di permukaan, mengunci metode yang disinkronkan, tetapi pada kenyataannya mengunci kelas. dieksekusi. Metode statis mirip dengan metode non-statis. Namun, metode statis dan metode non-statis tidak akan saling mempengaruhi, lihat kode berikut:
Public Class MyThread1 Extends {Public String MethoneName; ");} Metode void yang disinkronkan publik () {Metode (" Metode Non-Statis Metode2 ");} public static static void Method3 () {Method (" Metode Statis Metode ");} Metode Void Sinkronisasi Statis Publik4 () {Metode "Metode Statis4 Metode"); ) Lempar Exception {MyThread1 MyThread1 = MyThread1 () baru; ); Hasil berjalan adalah:
Metode Metode Non-Statis1 Metode Statis Metode3 Metode
Dari hasil yang dijalankan di atas, kita dapat melihat bahwa Method2 dan Method4 tidak akan berjalan sampai Method1 dan Method3 selesai. Oleh karena itu, kita dapat menarik kesimpulan bahwa jika kita menggunakan disinkronkan untuk mendefinisikan metode non-statis di kelas, itu akan mempengaruhi semua metode non-statis yang disinkronkan di kelas ini; Dalam metode ini metode statis yang ditentukan oleh disinkronkan. Ini sedikit seperti kunci tabel dalam tabel data. Oleh karena itu, penggunaan yang berat dari metode sinkronisasi ini akan sangat mengurangi kinerja program.
Kiat untuk akses yang lebih aman ke sumber daya bersama:
1. Tentukan variabel instance dari pribadi + metode GET, alih -alih mendefinisikan variabel instance publik/dilindungi. Jika suatu variabel didefinisikan sebagai publik, objek dapat secara langsung mendapatkannya memotong kontrol metode sinkronisasi di dunia luar dan mengubahnya. Ini juga salah satu implementasi standar Javabean.
2. Jika variabel instan adalah objek, seperti array atau arraylist, maka metode di atas masih tidak aman, karena ketika dunia luar mendapatkan referensi ke objek instan melalui metode GET dan mengarahkannya ke objek lain, lalu Variabel pribadi juga berubah, bukankah itu sangat berbahaya? Pada saat ini, Anda perlu menambahkan disinkronkan untuk mendapatkan metode dan hanya mengembalikan klon () dari objek pribadi ini. Dengan cara ini, apa yang didapat penelepon hanyalah referensi ke salinan objek.
Tiga cara untuk mendapatkan monitor objek (kunci) dan beri tahu ()
Dalam metode utas, panggilan untuk menunggu () dan memberi tahu () harus menentukan objek objek, dan utas harus memiliki monitor objek objek. Cara termudah untuk mendapatkan monitor objek adalah dengan menggunakan kata kunci yang disinkronkan pada objek. Setelah memanggil metode tunggu (), utas akan melepaskan kunci objek dan memasuki status tidur. Ketika utas lain memanggil metode notify (), objek objek yang sama harus digunakan.
Untuk beberapa metode yang dikunci oleh suatu objek, salah satunya akan dipilih untuk bangun saat memanggil metode notify (), dan notifyAll () akan membangunkan semua utas menunggu.
Paket net.mindview.util; import javax.swing.jframe; kelas publik WaitAndnotify {public static void main (string [] args) {System. frame.setDefaultCloseOperation (jFrame. Exit_on_close); 300, 100); {T = NEWNDNOTIFYTHREAD (WaitAndNotifyJFrame.ini); .add (mulai); (jeda); .add (akhir); = f; false; if (iswait) (); Seperti pada kode di kotak contoh di atas, jika blok kode sinkron dihapus, eksekusi akan melempar pengecualian java.lang.illegalmonitorStateException.
Melihat JDK, kita dapat melihat bahwa alasan pengecualian ini adalah bahwa utas saat ini bukan pemilik monitor objek ini.
Metode ini hanya boleh dipanggil oleh utas yang merupakan pemilik monitor objek ini.
1. Dengan mengeksekusi metode instance sinkron dari objek ini, seperti:
public disinkronkan void n () {notify (); 2. Dengan mengeksekusi badan pernyataan yang disinkronkan yang disinkronkan pada objek ini, seperti:
public void n () {disinkronkan (ini) {notify (); 3. Untuk objek jenis kelas, Anda dapat menjalankan metode statis sinkron dari kelas ini.
Saat memanggil metode statis, kami tidak perlu membuat objek instan. Oleh karena itu, ini tidak dapat digunakan untuk menyinkronkan metode statis, sehingga objek kelas harus digunakan untuk menyinkronkan metode statis. Contoh lain untuk diilustrasikan:
public class SynchronizedStatic implements Runnable { private static boolean flag = true;//Class object synchronization method one: // Pay attention to the synchronization method of static modification, monitor: SynchronizedStatic.class pri vate static synchronized void testSyncMethod() { for (int i = i <100; // Metode Sinkronisasi Objek Kelas 2: Private Void testsyncblock () {// Display menggunakan kelas mendapatkan sebagai monitor. Ini sama dengan metode tersinkronisasi statis secara implisit mendapatkan monitor kelas. Sinkronisasi (Kelas Sinkronisasi ("TestsyncBlock:" + i);}}} public void run () {// Flag adalah variabel statis. Oleh karena itu, utas yang berbeda akan menjalankan metode yang berbeda, dan hanya dengan cara ini Anda dapat melihat efek penguncian yang berbeda. if (flag = false; = SynchronizedStatic (); Kode di atas menjalankan hasil dari dua metode sinkronisasi untuk mencetak 100 angka dari 0 hingga 99 pada saat yang sama. Blokir. Kedua metode itu serupa. Karena ruang lingkup Metode Satu dan Metode Dua adalah Kelas, mereka saling eksklusif. Oleh karena itu, hasil yang berjalan dari program ini adalah:
testsyncmethod: 0testsyncmethod: 1 ... ... testsyncmethod: 99testsyncblock: 0 ... ... testsyncblock: 99
Namun, jika kita mengganti SynchronizedStatic.
testsyncblock: 0testsyncmethod: 0testsyncblock: 1testsyncmethod: 1 ... ... testsyncmethod: 99testsyncblock: 99
Ada dua lingkup kunci, satu adalah objek kelas dan yang lainnya adalah kelas itu sendiri. Dalam kode di atas, dua metode diberikan untuk membuat ruang lingkup kunci kelas, sehingga sinkronisasi dapat diselesaikan antara berbagai objek dari kelas yang sama.
Untuk meringkas hal -hal di atas, poin -poin berikut perlu dicatat:
1. tunggu (), beri tahu (), dan beri tahu All () semua perlu dieksekusi dengan premis memiliki monitor objek, jika tidak java.lang.illegalmonitorStateException akan dilemparkan.
2. Beberapa utas dapat menunggu satu objek secara bersamaan.
3. Notify () adalah untuk membangunkan utas secara acak menunggu objek.
4. Utas yang dibangunkan oleh notify () tidak bangun segera setelah notify () dieksekusi, tetapi hanya setelah utas notify () melepaskan monitor objek.
5. Metode objek ini masih jauh dari metode tidur dan interupsi Thread, jadi jangan bingung.