Semaphore adalah kelas yang biasa digunakan dalam paket JUC. Ini adalah aplikasi mode berbagi AQS. Ini dapat memungkinkan beberapa utas untuk beroperasi pada sumber daya bersama secara bersamaan dan dapat secara efektif mengontrol jumlah konkurensi. Itu dapat mencapai kontrol lalu lintas yang baik. Semaphore memberikan konsep lisensi, yang dapat dianggap sebagai tiket bus. Hanya mereka yang berhasil mendapatkan tiket yang bisa naik bus. Ada sejumlah tiket tertentu dan tidak mungkin untuk mengeluarkannya tanpa batasan, yang akan menyebabkan kelebihan bus. Jadi ketika tiket dikeluarkan (bus penuh), yang lain hanya bisa menunggu kereta berikutnya. Jika seseorang turun dari bus di tengah jalan, posisinya akan gratis, jadi jika orang lain ingin naik bus saat ini, mereka bisa mendapatkan tiket lagi. Berbagai kumpulan dapat diimplementasikan menggunakan semaphore. Di akhir artikel ini, kami akan menulis kumpulan koneksi basis data sederhana. Pertama, mari kita lihat konstruktor Semaphore.
// Konstruktor 1Public Semaphore (Int izin) {sync = nonfairsync baru (izin);} // Konstruktor 2Public Semaphore (Int Izin, Boolean Fair) {Sync = Fair? New FairSync (Izin): nonfairsync baru (izin);}Semaphore menyediakan dua konstruktor bebas parameter, tetapi tidak ada konstruktor bebas parameter yang disediakan. Kedua konstruktor harus lulus dalam jumlah awal lisensi. Semaphore yang dibangun menggunakan konstruktor 1 akan diperoleh dengan cara yang tidak fair saat mendapatkan lisensi. Menggunakan Konstruktor 2 dapat menentukan metode untuk mendapatkan lisensi melalui parameter (adil atau tidak adil). Semaphore terutama menyediakan dua jenis API ke dunia luar, mendapatkan lisensi dan melepaskan lisensi. Standarnya adalah untuk mendapatkan dan melepaskan satu lisensi, dan parameter juga dapat diteruskan untuk mendapatkan dan melepaskan beberapa lisensi secara bersamaan. Dalam artikel ini, kita hanya akan berbicara tentang situasi memperoleh dan merilis satu lisensi setiap kali.
1. Dapatkan lisensi
// Dapatkan lisensi (interupsi respons) void public aquire () melempar interruptedException {sync.acquireSharedIntricly (1);} // Dapatkan lisensi (tidak merespons ke interupsi) public void AcquireUnterricture () {sync.acquireshared (1);} // mencoba untuk mendapatkan lisensi (nonfair) {nonfairition) {nonfairition) {nonfairiceted (1);} // mencoba untuk mendapatkan lisensi (nonfair sync.nonfairtryacquireshared (1)> = 0;} // Cobalah untuk mendapatkan lisensi (waktu lama, unit timeunit) melempar interruptedException {return sync.tryacquiresharednanos (1, unit.tonanos (timeout));}API di atas adalah operasi akuisisi lisensi default yang disediakan oleh Semaphore. Hanya mendapatkan satu lisensi pada satu waktu juga merupakan situasi umum dalam kehidupan nyata. Selain pengambilan langsung, ini juga memberikan upaya untuk mengambil. Operasi pengambilan langsung dapat memblokir utas setelah kegagalan, saat mencoba mengambil tidak akan. Perlu juga dicatat bahwa metode tryacquire digunakan untuk mencoba mendapatkannya dengan cara yang tidak adil. Apa yang sering kita gunakan dalam waktu normal adalah mendapatkan lisensi. Mari kita lihat bagaimana itu diperoleh. Anda dapat melihat bahwa metode Acquire secara langsung memanggil Sync.AcquiresharedIbrnya (1). Metode ini adalah metode dalam AQS. Kami pernah berbicara tentang artikel seri kode sumber AQS. Mari kita tinjau lagi.
// memperoleh kunci dalam mode interruptable (mode bersama) public final void AcquireSharedIbrnya (int arg) melempar InterruptedException {// Tentukan apakah utas tersebut terganggu, jika demikian, lempar pengecualian jika (thread.echerrupted ()) {lempar interruptEcception baru (); } // 1. Cobalah untuk mendapatkan kunci jika (TRYACQUIRESHARED (arg) <0) {// 2. Jika akuisisi gagal, masukkan metode doAcquiresharedIbrnya (arg); }}Metode pertama AcquireSharedIntrayruptibly adalah memanggil metode tryacquireshared untuk mencoba memperoleh. Tryacquireshared adalah metode abstrak dalam AQS. Dua kelas turunan Fairsync dan Nonfairsync mengimplementasikan logika metode ini. Fairsync mengimplementasikan logika akuisisi yang wajar, sementara nonfairsync mengimplementasikan logika akuisisi non-fair.
Sinkronisasi kelas statis abstrak memperluas abstrak abstrakqueuedsynchronizer {// coba untuk mendapatkan int nonfairtryacquireshared (int intquires) {for (;;) {// dapatkan lisensi yang tersedia int tersedia = getState (); // Dapatkan lisensi yang tersisa int tetap = tersedia - memperoleh; // 1. Jika tetap kurang dari 0, kembalikan tersisa secara langsung // 2. Jika tetap lebih besar dari 0, perbarui status sinkronisasi terlebih dahulu dan kemudian mengembalikan IF yang tersisa (tetap <0 || CompareEndsetState (tersedia, tersisa)) {return tetap; }}}} // Nonfairsync Static Final Class NonFairSync Memperluas Sinkronisasi {private static final long serialVersionuid = -2694183684443567898L; Nonfairsync (int ijin) {super (izin); } // Cobalah untuk mendapatkan lisensi yang dilindungi int tryacquireshared (int mengakuisisi) {return nonfairtryacquireshared (mengakuisisi); }} // fair synchronizer static class final fairsync memperluas sinkronisasi {private static final long serialversionuid = 2014338818796000944l; Fairsync (int ijin) {super (izin); } // Cobalah untuk mendapatkan lisensi yang dilindungi int tryacquireshared (int mengakuisisi) {for (;;) {// menilai apakah ada orang di depan antrian sinkronisasi jika (hasqueuedpredecessors ()) {// Jika ada, return -1, yang menunjukkan bahwa upaya untuk mendapatkan pengembalian yang gagal -1; } // Dapatkan lisensi yang tersedia int tersedia = getState (); // Dapatkan lisensi yang tersisa int tetap = tersedia - memperoleh; // 1. Jika tetap kurang dari 0, kembalikan langsung ke sisa // 2. Jika tetap lebih besar dari 0, status sinkronisasi akan diperbarui terlebih dahulu dan kemudian dikembalikan ke tetap jika (tetap <0 || compareEndsetState (tersedia, tersisa)) {return tetap; }}}}Perlu dicatat di sini bahwa metode nonfairsync secara langsung memanggil metode nonfairtryacquireshared, yang ada dalam sinkronisasi kelas induk. Logika kunci akuisisi non-fair adalah untuk pertama-tama mengambil keadaan sinkronisasi saat ini (keadaan sinkron mewakili jumlah lisensi), kurangi parameter keadaan sinkronisasi saat ini. Jika hasilnya tidak kurang dari 0, terbukti bahwa masih ada lisensi yang tersedia, maka nilai status sinkronisasi diperbarui secara langsung menggunakan operasi CAS, dan akhirnya, nilai hasilnya akan dikembalikan terlepas dari apakah hasilnya kurang dari 0. Di sini kita perlu memahami arti dari nilai pengembalian metode Tryacquireshared. Mengembalikan angka negatif berarti akuisisi gagal, nol berarti utas saat ini berhasil diperoleh tetapi utas berikutnya tidak dapat diperoleh lagi, dan angka positif berarti utas saat ini berhasil diperoleh dan utas selanjutnya juga dapat diperoleh. Mari kita lihat kode AcquireSharedInterricture Method.
// memperoleh kunci dalam mode interruptible (mode bersama) public final void AcquireSharedIbrnya (int arg) melempar InterruptedException {// Tentukan apakah utas tersebut terganggu, jika demikian, lempar pengecualian jika (thread.echerrupted ()) {lempar interruptEcception baru (); } // 1. Cobalah untuk memperoleh kunci // angka negatif: menunjukkan bahwa akuisisi gagal // nilai nol: menunjukkan bahwa utas saat ini berhasil diperoleh, tetapi utas berikutnya tidak dapat lagi mendapatkan // angka positif: menunjukkan bahwa utas saat ini berhasil diperoleh, dan utas berikutnya juga dapat memperoleh keberhasilan jika (tryacquireshared (arg) <0) {// 2. Jika akuisisi gagal, masukkan metode doAcquiresharedIbrnya (arg); }}Jika sisa yang dikembalikan kurang dari 0, itu berarti akuisisi gagal. Oleh karena itu, tryacquireshared (arg) <0 adalah benar, jadi metode doacquiresharedertibly akan disebut selanjutnya. Ketika kami berbicara tentang AQS, itu akan membungkus utas saat ini ke dalam sebuah node dan memasukkannya ke dalam ekor antrian sinkronisasi, dan dimungkinkan untuk menangguhkan utas. Ini juga merupakan alasan mengapa utas akan mengantri dan diblokir ketika tetap kurang dari 0. Jika sisa yang dikembalikan> = 0 berarti bahwa utas saat ini telah berhasil diperoleh. Oleh karena itu, tryacquireshared (arg) <0 adalah flase, sehingga metode doacquiresharedIbrnya tidak akan lagi dipanggil untuk memblokir utas saat ini. Di atas adalah seluruh logika akuisisi yang tidak adil. Saat akuisisi yang adil, Anda hanya perlu memanggil metode HasqueuedPredecessors sebelum ini untuk menentukan apakah seseorang mengantri dalam antrian sinkronisasi. Jika demikian, kembalikan -1 secara langsung menunjukkan bahwa akuisisi gagal, jika tidak, langkah -langkah berikut dilanjutkan sebagai akuisisi yang tidak adil.
2. Lepaskan lisensi
// Rilis lisensi rilis public void () {sync.releaseshared (1);}Memanggil metode rilis adalah untuk merilis lisensi. Operasinya sangat sederhana, jadi kami menyebut metode AQS yang dilepaskan. Mari kita lihat metode ini.
// Operasi Kunci Rilis (Mode Bersama) Publik Boolean Releaseshared (int arg) {// 1. Coba lepaskan kunci if (tryreleaseshared (arg)) {// 2. Jika rilis berhasil, bangun utas lain Doreleaseshared (); Kembali Benar; } return false;}Metode Releaseshared dari AQS pertama memanggil metode tryreleaseshared untuk mencoba melepaskan kunci. Logika implementasi dari metode ini adalah dalam sinkronisasi subkelas.
Sinkronisasi kelas statis abstrak memperluas abstrak abstrakqueuedsynchronizer {... // Cobalah untuk melepaskan operasi boolean final yang dilindungi tryreleaseshared (rilis int) {for (;;) {// Dapatkan status sinkronisasi saat ini int Current = getState (); // ditambah status sinkronisasi saat ini sebagai berikut int next = arus + rilis; // Jika hasil penambahan kurang dari keadaan sinkronisasi saat ini, kesalahan akan dilaporkan jika (berikutnya <saat ini) {melempar kesalahan baru ("jumlah izin maksimum terlampaui"); } // Perbarui nilai status sinkronisasi dalam mode CAS, dan kembalikan true jika pembaruan berhasil, jika tidak terus loop jika (compareEndsetState (saat ini, selanjutnya)) {return true; }}} ...}Anda dapat melihat bahwa metode tryreleaseshared menggunakan loop untuk berputar. Pertama, dapatkan status sinkronisasi, tambahkan parameter yang masuk, dan kemudian perbarui status sinkronisasi di CAS. Jika pembaruan berhasil, kembalikan true dan lompat keluar dari metode ini. Kalau tidak, loop akan berlanjut sampai berhasil. Ini adalah proses semaphore melepaskan lisensi.
3. Tulis kumpulan koneksi secara manual
Kode semaphore tidak terlalu rumit. Operasi yang umum digunakan adalah untuk mendapatkan dan melepaskan lisensi. Logika implementasi operasi ini relatif sederhana, tetapi ini tidak menghalangi aplikasi semaphore yang meluas. Selanjutnya, kami akan menggunakan Semaphore untuk mengimplementasikan kumpulan koneksi basis data sederhana. Melalui contoh ini, kami berharap pembaca dapat memiliki pemahaman yang lebih dalam tentang penggunaan Semaphore.
kelas publik ConnectPool {// Koneksi Ukuran Pool Ukuran int Private; // Koleksi Koneksi Basis Data Private Connect [] Connects; // status koneksi bendera private boolean [] ConnectFlag; // sisa jumlah koneksi yang tersedia int private volatile tersedia; // Semaphore Private Semaphore Semaphore; // konstruktor public connectpool (ukuran int) {this.size = size; this.available = size; semaphore = semaphore baru (ukuran, true); Connects = New Connect [size]; ConnectFlag = Boolean baru [ukuran]; initConnects (); } // Inisialisasi koneksi private void initConnects () {// menghasilkan sejumlah koneksi basis data yang ditentukan untuk (int i = 0; i <this.size; i ++) {connects [i] = new connect (); }} // Dapatkan koneksi basis data privat disinkronkan connect getConnect () {for (int i = 0; i <connectFlag.length; i ++) {// Transfer koleksi untuk menemukan koneksi yang tidak digunakan jika (! ConnectFlag [i]) {// Atur koneksi ke dalam menggunakan ConnectFlag [i] = true; // Kurangi jumlah koneksi yang tersedia yang tersedia--; System.out.println ("【"+thread.currentThread (). GetName ()+"】 untuk mendapatkan jumlah koneksi yang tersisa:"+tersedia); // return Referensi koneksi Return Connects [i]; }} return null; } // Dapatkan koneksi public connect openconnect () melempar InterruptedException {// Dapatkan lisensi semaphore.acquire (); // Dapatkan koneksi database return getConnect (); } // Rilis rilis void yang disinkronkan publik koneksi (connect connect) {for (int i = 0; i <this.size; i ++) {if (connect == Connects [i]) {// Atur koneksi yang tidak digunakan connectFlag [i] = false; // Tambahkan 1 nomor koneksi yang tersedia; System.out.println ("【"+thread.currentThread (). GetName ()+"] untuk melepaskan nomor koneksi yang tersisa:"+tersedia); // melepaskan lisensi semaphore.release (); }}} // Tambahkan jumlah koneksi yang tersedia int int tersedia () {return tersedia; }}Kode Uji:
TestThread kelas publik memperluas thread {private static connectPool pool = new connectpool (3); @Override public void run () {coba {connect connect = pool.openconnect (); Thread.sleep (100); // ambil break pool.release (hubungkan); } catch (InterruptedException e) {E.PrintStackTrace (); }} public static void main (string [] args) {for (int i = 0; i <10; i ++) {new testThread (). start (); }}}Hasil tes:
Kami menggunakan array untuk menyimpan referensi ke koneksi basis data. Saat menginisialisasi kumpulan koneksi, kami akan memanggil metode initConnects untuk membuat sejumlah koneksi basis data yang ditentukan dan menyimpan referensi mereka dalam array. Selain itu, ada array dengan ukuran yang sama untuk merekam apakah koneksi tersedia. Setiap kali utas eksternal meminta untuk mendapatkan koneksi, pertama hubungi metode semaphore.acquire () untuk mendapatkan lisensi, lalu atur status koneksi untuk digunakan, dan akhirnya kembalikan referensi ke koneksi. Jumlah lisensi ditentukan oleh parameter yang dilewati selama konstruksi. Jumlah lisensi dikurangi dengan 1 setiap kali metode semaphore.aCquire () dipanggil. Ketika angka dikurangi menjadi 0, itu berarti tidak ada koneksi yang tersedia. Pada saat ini, jika utas lain mendapatkannya lagi, itu akan diblokir. Setiap kali utas melepaskan koneksi, semaphore.release () akan dipanggil untuk melepaskan lisensi. Pada saat ini, jumlah total lisensi akan meningkat lagi, yang berarti bahwa jumlah koneksi yang tersedia telah meningkat. Kemudian utas yang diblokir sebelumnya akan bangun dan terus mendapatkan koneksi. Pada saat ini, Anda dapat berhasil mendapatkan koneksi dengan mendapatkannya lagi. Dalam contoh pengujian, kumpulan koneksi 3 koneksi diinisialisasi. Kita dapat melihat dari hasil tes bahwa setiap kali utas memperoleh koneksi, jumlah koneksi yang tersisa akan dikurangi dengan 1. Ketika utas dikurangi menjadi 0, utas lain tidak dapat lagi mendapatkannya. Saat ini, Anda harus menunggu utas untuk melepaskan koneksi sebelum terus mendapatkannya. Anda dapat melihat bahwa jumlah koneksi yang tersisa selalu berubah antara 0 dan 3, yang berarti bahwa tes kami berhasil.
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.