Jika yayasan java Anda lemah atau Anda tidak memiliki pemahaman yang baik tentang java multithreading, silakan baca artikel ini "Pelajari definisi utas, status dan properti java multithreading"
Sinkronisasi selalu menjadi poin yang sulit bagi Java multi-threading, dan jarang digunakan ketika kita melakukan pengembangan android, tetapi ini bukan alasan mengapa kita tidak terbiasa dengan sinkronisasi. Saya harap artikel ini dapat memungkinkan lebih banyak orang untuk memahami dan menerapkan sinkronisasi Java.
Dalam aplikasi multi-threaded, dua atau lebih utas perlu berbagi akses ke data yang sama. Ini biasanya menjadi kondisi balapan jika dua utas mengakses objek yang sama dan setiap utas memanggil metode yang memodifikasi objek.
Contoh termudah dari kondisi kompetisi adalah: misalnya, tiket kereta api pasti, tetapi ada jendela untuk menjual tiket kereta di mana -mana, setiap jendela setara dengan satu utas, dan begitu banyak utas berbagi semua sumber daya tiket kereta api. Dan tidak dapat menjamin atomisitasnya. Jika dua utas menggunakan sumber ini pada suatu saat, tiket kereta yang mereka ambil adalah sama (angka kursi sama), yang akan menyebabkan masalah bagi penumpang. Solusinya adalah ketika sebuah utas ingin menggunakan sumber daya tiket kereta api, kami memberikan kunci, dan setelah selesai bekerja, kami akan memberikan kunci ke utas lain yang ingin menggunakan sumber daya ini. Dengan cara ini, situasi di atas tidak akan terjadi.
1. Kunci objek
Kata kunci yang disinkronkan secara otomatis menyediakan kunci dan kondisi terkait. Sangat nyaman untuk menggunakan disinkronkan dalam banyak kasus di mana kunci eksplisit diperlukan. Namun, ketika kita memahami kelas reentrantlock dan objek bersyarat, kita dapat lebih memahami kata kunci yang disinkronkan. Reentrantlock diperkenalkan di Java SE 5.0. Struktur blok kode dilindungi menggunakan reentrantlock sebagai berikut:
mlock.lock (); coba {...} akhirnya {mlock.unlock ();}Struktur ini memastikan bahwa hanya satu utas yang memasuki area kritis kapan saja. Setelah utas memblokir objek kunci, tidak ada utas lain yang dapat melewati pernyataan kunci. Ketika utas lain memanggil kunci, mereka diblokir sampai utas pertama melepaskan objek kunci. Sangat penting untuk memasukkan operasi pembukaan akhirnya. Jika pengecualian terjadi di area kritis, kunci harus dilepaskan, jika tidak utas lain akan diblokir selamanya.
2. Objek bersyarat <br /> Saat memasuki area kritis, ditemukan bahwa ia hanya dapat dieksekusi setelah kondisi tertentu dipenuhi. Gunakan objek bersyarat untuk mengelola utas yang telah memperoleh kunci tetapi tidak dapat melakukan pekerjaan yang bermanfaat. Objek bersyarat juga disebut variabel bersyarat.
Mari kita lihat contoh berikut untuk melihat mengapa objek bersyarat diperlukan
Misalkan dalam skenario kita perlu menggunakan transfer bank, pertama -tama kita menulis kelas bank, dan konstruktornya perlu ditransfer ke jumlah akun dan jumlah akun.
Bank Kelas Publik {Private Double [] akun; Private Lock Banklock; bank umum (int n, double initialAnce) {Accounts = New Double [n]; banklock = baru reentrantlock (); untuk (int i = 0; i <accounts.length; i ++) {Accounts [i] = InitialBalance; }}}Selanjutnya kami ingin menarik uang dan menulis metode penarikan. Dari adalah transferor, ke adalah penerima, dan jumlah jumlah transfer. Akibatnya, kami menemukan bahwa saldo transferor tidak cukup. Jika utas lain menghemat cukup uang ke transferor, transfer bisa berhasil. Namun, utas ini telah memperoleh kunci, yang eksklusif, dan utas lainnya tidak dapat memperoleh kunci untuk melakukan operasi setoran. Inilah sebabnya mengapa kita perlu memperkenalkan objek bersyarat.
transfer public void (int from, int ke, int, int) {banklock.lock (); coba {while (akun [dari] <jumlah) {// tunggu}} akhirnya {banklock.unlock (); }}Objek kunci memiliki beberapa objek kondisi terkait. Anda dapat menggunakan metode NewCondition untuk mendapatkan objek kondisi. Setelah kami mendapatkan objek kondisi, kami memanggil metode menunggu, dan utas saat ini diblokir dan kuncinya ditinggalkan.
Bank Kelas Publik {Private Double [] akun; Private Lock Banklock; kondisi kondisi pribadi; bank umum (int n, double initialAnce) {Accounts = New Double [n]; banklock = baru reentrantlock (); // Dapatkan kondisi kondisi objek = banklock.newcondition (); untuk (int i = 0; i <accounts.length; i ++) {Accounts [i] = InitialBalance; }} public void Transfer (int from, int ke, int, int) melempar interruptedException {banklock.lock (); coba {while (akun [dari] <jumlah) {// blokir utas saat ini dan nyalakan kondisi kunci.AWAIT (); }} akhirnya {banklock.unlock (); }}} Utas menunggu kunci pada dasarnya berbeda dari utas yang memanggil metode menunggu. Setelah utas memanggil metode AWAIT, itu akan memasukkan set tunggu kondisi itu. Ketika kunci tersedia, utas tidak dapat segera membuka kunci, tetapi sebaliknya dalam keadaan pemblokiran sampai utas lain memanggil metode SinyalAll pada kondisi yang sama. Ketika utas lain siap untuk mentransfer uang ke transferor kami sebelumnya, cukup panggil kondisi.signalall (); Panggilan ini akan mengaktifkan kembali semua utas menunggu kondisi ini.
Ketika sebuah utas memanggil metode AWAIT, itu tidak dapat mengaktifkan kembali dirinya sendiri dan berharap bahwa utas lain akan memanggil metode SignalAll untuk mengaktifkan dirinya sendiri. Jika tidak ada utas lain yang mengaktifkan utas menunggu, maka kebuntuan akan terjadi. Jika semua utas lain diblokir, dan panggilan utas aktif terakhir menunggu sebelum membuka blokir utas lainnya, itu juga akan diblokir. Tidak ada utas yang dapat membuka blokir utas lain dan program akan ditangguhkan.
Lalu kapan sinyal akan dipanggil? Biasanya, harus bermanfaat untuk memanggil sinyal semua saat menunggu arah utas berubah. Dalam contoh ini, ketika saldo akun berubah, utas menunggu harus memiliki kesempatan untuk memeriksa saldo.
Transfer public void (int from, int ke, int, int) melempar InterruptedException {banklock.lock (); coba {while (akun [dari] <jumlah) {// blokir utas saat ini dan nyalakan kondisi kunci.AWAIT (); } // transfer operasi ... condition.signalall (); } akhirnya {banklock.unlock (); }}Ketika metode SignalAll dipanggil, utas menunggu tidak segera diaktifkan. Ini hanya membuka blokir utas yang menunggu sehingga utas ini dapat mencapai akses ke objek dengan bersaing setelah utas saat ini keluar dari metode sinkron. Metode lain adalah sinyal, yang secara acak membuka blokir utas. Jika utas masih tidak dapat dijalankan, itu akan diblokir lagi. Jika tidak ada sinyal panggilan utas lain lagi, sistem akan menemui jalan buntu.
3. Kata kunci yang disinkronkan
Antarmuka kunci dan kondisi memberikan programmer dengan tingkat kontrol penguncian yang tinggi, dalam kebanyakan kasus, kontrol tersebut tidak diperlukan, dan mekanisme yang tertanam dalam bahasa Java dapat digunakan. Mulai dari Java versi 1.0, setiap objek di Java memiliki kunci internal. Jika metode dinyatakan dengan kata kunci yang disinkronkan, kunci objek akan melindungi seluruh metode. Artinya, untuk memanggil metode ini, utas harus mendapatkan kunci objek internal.
Dengan kata lain,
Metode void yang disinkronkan publik () {}Setara dengan
Metode public void () {this.lock.lock (); coba {} akhirnya {this.lock.unlock ();} Dalam contoh bank di atas, kita dapat mendeklarasikan metode transfer kelas bank sebagai disinkronkan alih -alih menggunakan kunci yang ditampilkan.
Hanya ada satu kondisi terkait untuk kunci objek internal. Tunggu pembesaran ditambahkan ke utas ke set tunggu. Beri tahu semua atau beri tahu metode buka blokir utas menunggu. Dengan kata lain, tunggu setara dengan calon condition.Await (), NotifyAll setara dengan condition.signalall ();
Metode transfer contoh kami di atas juga dapat ditulis seperti ini:
transfer void yang disinkronkan publik (int from, int ke, jumlah int) melempar interruptedException {while (akun [dari] <jumlah) {tunggu (); } // Operasi transfer ... notifyall (); }Anda dapat melihat bahwa menggunakan kata kunci yang disinkronkan untuk menulis kode jauh lebih sederhana. Tentu saja, untuk memahami kode ini, Anda harus memahami bahwa setiap objek memiliki kunci internal dan bahwa kunci memiliki kondisi internal. Kunci mengelola utas yang mencoba memasukkan metode yang disinkronkan, dan kondisi mengelola utas yang menelepon menunggu.
4. Pemblokiran Sinkron <BR /> di atas kami mengatakan bahwa setiap objek Java memiliki kunci, dan utas dapat memanggil metode sinkronisasi untuk mendapatkan kunci, dan ada mekanisme lain untuk mendapatkan kunci. Dengan memasukkan blok sinkronisasi, ketika utas memasuki bentuk pemblokiran berikut:
disinkronkan (obj) {}Jadi dia mendapatkan kunci OBJ. Mari kita lihat kelas bank
Public Class Bank {Private Double [] akun; Private Object Lock = New Object (); bank umum (int n, double initialAnce) {Accounts = New Double [n]; untuk (int i = 0; i <accounts.length; i ++) {Accounts [i] = InitialBalance; }} public void transfer (int from, int ke, jumlah int) {disinkronkan (lock) {// transfer operasi ...}}}Di sini, pembuatan objek kunci hanya digunakan untuk menggunakan kunci yang dipegang oleh setiap objek Java. Terkadang pengembang menggunakan kunci objek untuk mengimplementasikan operasi atom tambahan, yang disebut penguncian klien. Misalnya, kelas vektor, metodenya sinkron. Sekarang asumsikan bahwa saldo bank disimpan dalam vektor
Transfer public void (vector <double> akun, int from, int ke, jumlah int) {accounts.set (from, accounts.get (from) -Amount); Accounts.set (ke, Accounts.get (ke)+Jumlah;}Metode Get and Set dari kelas Vecror adalah sinkron, tetapi ini tidak membantu kami. Setelah panggilan pertama untuk selesai selesai, sangat mungkin bahwa satu utas ditolak hak untuk berjalan dalam metode transfer, jadi utas lain mungkin telah menyimpan nilai yang berbeda di lokasi penyimpanan yang sama, tetapi kami dapat mencegat kunci ini
transfer public void (vector <mouble> akun, int from, int ke, jumlah int) {disinkronkan (akun) {accounts.set (from, accounts.get (from) -amount); accounts.set (ke, accounts.get (ke)+jumlah;}}Penguncian klien (blok kode sinkron) sangat rapuh dan biasanya tidak dianjurkan. Secara umum, yang terbaik adalah menggunakan kelas yang disediakan di bawah paket java.util.concurrent, seperti memblokir antrian. Jika metode sinkronisasi cocok untuk program Anda, coba gunakan metode sinkronisasi. Ini dapat mengurangi jumlah kode yang ditulis dan mengurangi kemungkinan kesalahan. Jika Anda perlu menggunakan fitur unik yang disediakan oleh struktur kunci/kondisi, hanya menggunakan kunci/kondisi.
Di atas adalah semua tentang artikel ini, saya harap ini akan membantu untuk pembelajaran semua orang.