Adakah yang bisa memberi tahu saya keuntungan metode tersinkronisasi dari blok tersinkronisasi dengan contoh?
Adakah yang bisa memberi tahu saya keuntungan metode tersinkronisasi dari blok tersinkronisasi dengan contoh?
Jawaban:
Adakah yang bisa memberi tahu saya keuntungan dari metode yang disinkronkan dari blok yang disinkronkan dengan sebuah contoh? Terima kasih.
Tidak ada keuntungan jelas menggunakan metode tersinkronisasi di atas blok.
Mungkin satu-satunya (tapi saya tidak akan menyebutnya keuntungan) adalah Anda tidak perlu memasukkan referensi objek this
.
Metode:
public synchronized void method() { // blocks "this" from here....
...
...
...
} // to here
Blok:
public void method() {
synchronized( this ) { // blocks "this" from here ....
....
....
....
} // to here...
}
Lihat? Tidak ada keuntungan sama sekali.
Blok memang memiliki keunggulan dibandingkan metode, sebagian besar dalam fleksibilitas karena Anda dapat menggunakan objek lain sebagai kunci sedangkan sinkronisasi metode akan mengunci seluruh objek.
Membandingkan:
// locks the whole object
...
private synchronized void someInputRelatedWork() {
...
}
private synchronized void someOutputRelatedWork() {
...
}
vs.
// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();
private void someInputRelatedWork() {
synchronized(inputLock) {
...
}
}
private void someOutputRelatedWork() {
synchronized(outputLock) {
...
}
}
Juga jika metode ini bertambah, Anda masih dapat memisahkan bagian yang disinkronkan:
private void method() {
... code here
... code here
... code here
synchronized( lock ) {
... very few lines of code here
}
... code here
... code here
... code here
... code here
}
synchronized
blok diimplementasikan dengan menggunakan dua instruksi, monitorenter
dan monitorexit
, ditambah handler pengecualian yang menjamin bahwa monitorexit
disebut bahkan dalam kasus luar biasa. Itu semua disimpan saat menggunakan synchronized
metode.
Satu-satunya perbedaan nyata adalah bahwa blok yang disinkronkan dapat memilih objek yang disinkronkan. Metode yang disinkronkan hanya dapat menggunakan 'this'
(atau instance Kelas yang sesuai untuk metode kelas yang disinkronkan). Misalnya, ini setara secara semantik:
synchronized void foo() {
...
}
void foo() {
synchronized (this) {
...
}
}
Yang terakhir ini lebih fleksibel karena dapat bersaing untuk mengunci terkait objek apa pun , sering kali variabel anggota. Ini juga lebih terperinci karena Anda bisa memiliki kode bersamaan mengeksekusi sebelum dan sesudah blok tetapi masih dalam metode. Tentu saja, Anda dapat dengan mudah menggunakan metode yang disinkronkan dengan refactoring kode konkuren menjadi metode terpisah yang tidak disinkronkan. Gunakan mana saja yang membuat kode lebih mudah dipahami.
Pro:
Cons:
Pro:
Cons:
Secara pribadi saya lebih suka menggunakan metode yang disinkronkan dengan kelas yang difokuskan hanya untuk hal yang membutuhkan sinkronisasi. Kelas tersebut harus sekecil mungkin dan karenanya harus mudah untuk meninjau sinkronisasi. Orang lain tidak perlu peduli dengan sinkronisasi.
Perbedaan utama adalah bahwa jika Anda menggunakan blok yang disinkronkan Anda dapat mengunci pada objek selain ini yang memungkinkan untuk menjadi jauh lebih fleksibel.
Asumsikan Anda memiliki antrian pesan dan banyak produsen dan konsumen pesan. Kami tidak ingin produsen saling mengganggu, tetapi konsumen harus dapat mengambil pesan tanpa harus menunggu produsen. Jadi kami hanya membuat objek
Object writeLock = new Object();
Dan mulai sekarang setiap kali seorang produsen ingin menambahkan pesan baru, kami hanya menguncinya:
synchronized(writeLock){
// do something
}
Jadi konsumen mungkin masih membaca, dan produsen akan dikunci.
Metode yang disinkronkan
Metode yang disinkronkan memiliki dua efek.
Pertama, ketika satu utas mengeksekusi metode yang disinkronkan untuk suatu objek, semua utas lain yang memanggil metode yang disinkronkan untuk blok objek yang sama (menunda eksekusi) sampai utas pertama selesai dengan objek.
Kedua, ketika metode yang disinkronkan keluar, itu secara otomatis membangun hubungan yang terjadi sebelum dengan setiap doa berikutnya dari metode yang disinkronkan untuk objek yang sama. Ini menjamin bahwa perubahan keadaan objek terlihat oleh semua utas.
Perhatikan bahwa konstruktor tidak dapat disinkronkan - menggunakan kata kunci yang disinkronkan dengan konstruktor adalah kesalahan sintaksis. Menyinkronkan konstruktor tidak masuk akal, karena hanya utas yang membuat objek harus memiliki akses ke sana ketika sedang dibangun.
Pernyataan Tersinkronisasi
Tidak seperti metode yang disinkronkan, pernyataan yang disinkronkan harus menentukan objek yang menyediakan kunci intrinsik: Paling sering saya menggunakan ini untuk menyinkronkan akses ke daftar atau peta tetapi saya tidak ingin memblokir akses ke semua metode objek.
T: Kunci dan Sinkronisasi Intrinsik Sinkronisasi dibangun di sekitar entitas internal yang dikenal sebagai kunci intrinsik atau kunci monitor. (Spesifikasi API sering merujuk ke entitas ini hanya sebagai "monitor.") Kunci intrinsik berperan dalam kedua aspek sinkronisasi: menegakkan akses eksklusif ke keadaan objek dan membangun hubungan sebelum terjadi yang penting untuk visibilitas.
Setiap objek memiliki kunci intrinsik yang terkait dengannya. Dengan konvensi, utas yang memerlukan akses eksklusif dan konsisten ke bidang objek harus memperoleh kunci intrinsik objek sebelum mengaksesnya, dan kemudian melepaskan kunci intrinsik ketika dilakukan dengan mereka. Sebuah thread dikatakan memiliki kunci intrinsik antara waktu kunci tersebut diperoleh dan melepaskan kunci tersebut. Selama utas memiliki kunci intrinsik, tidak ada utas lain yang bisa mendapatkan kunci yang sama. Utas lainnya akan memblokir ketika mencoba untuk mendapatkan kunci.
package test;
public class SynchTest implements Runnable {
private int c = 0;
public static void main(String[] args) {
new SynchTest().test();
}
public void test() {
// Create the object with the run() method
Runnable runnable = new SynchTest();
Runnable runnable2 = new SynchTest();
// Create the thread supplying it with the runnable object
Thread thread = new Thread(runnable,"thread-1");
Thread thread2 = new Thread(runnable,"thread-2");
// Here the key point is passing same object, if you pass runnable2 for thread2,
// then its not applicable for synchronization test and that wont give expected
// output Synchronization method means "it is not possible for two invocations
// of synchronized methods on the same object to interleave"
// Start the thread
thread.start();
thread2.start();
}
public synchronized void increment() {
System.out.println("Begin thread " + Thread.currentThread().getName());
System.out.println(this.hashCode() + "Value of C = " + c);
// If we uncomment this for synchronized block, then the result would be different
// synchronized(this) {
for (int i = 0; i < 9999999; i++) {
c += i;
}
// }
System.out.println("End thread " + Thread.currentThread().getName());
}
// public synchronized void decrement() {
// System.out.println("Decrement " + Thread.currentThread().getName());
// }
public int value() {
return c;
}
@Override
public void run() {
this.increment();
}
}
Periksa silang keluaran yang berbeda dengan metode yang disinkronkan, blokir dan tanpa sinkronisasi.
Catatan: metode dan blok tersinkronisasi statis berfungsi pada objek Class.
public class MyClass {
// locks MyClass.class
public static synchronized void foo() {
// do something
}
// similar
public static void foo() {
synchronized(MyClass.class) {
// do something
}
}
}
Ketika kompiler java mengubah kode sumber Anda menjadi kode byte, ia menangani metode yang disinkronkan dan blok yang disinkronkan sangat berbeda.
Ketika JVM mengeksekusi metode yang disinkronkan, thread yang mengeksekusi mengidentifikasi bahwa metode method_info metode memiliki set flag ACC_SYNCHRONIZED, kemudian secara otomatis memperoleh kunci objek, memanggil metode, dan melepaskan kunci. Jika pengecualian terjadi, utas secara otomatis melepaskan kunci.
Menyinkronkan blok metode, di sisi lain, memotong dukungan bawaan JVM untuk memperoleh kunci objek dan penanganan pengecualian dan mengharuskan fungsi dituliskan secara eksplisit dalam kode byte. Jika Anda membaca kode byte untuk metode dengan blok yang disinkronkan, Anda akan melihat lebih dari selusin operasi tambahan untuk mengelola fungsi ini.
Ini menunjukkan panggilan untuk menghasilkan metode yang disinkronkan dan blok yang disinkronkan:
public class SynchronizationExample {
private int i;
public synchronized int synchronizedMethodGet() {
return i;
}
public int synchronizedBlockGet() {
synchronized( this ) {
return i;
}
}
}
The synchronizedMethodGet()
Metode menghasilkan kode byte berikut:
0: aload_0
1: getfield
2: nop
3: iconst_m1
4: ireturn
Dan inilah kode byte dari synchronizedBlockGet()
metode ini:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: getfield
6: nop
7: iconst_m1
8: aload_1
9: monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
Salah satu perbedaan yang signifikan antara metode dan blok yang disinkronkan adalah bahwa, Blok yang disinkronkan umumnya mengurangi cakupan kunci. Karena ruang lingkup kunci berbanding terbalik dengan kinerja, selalu lebih baik untuk mengunci hanya bagian penting dari kode. Salah satu contoh terbaik menggunakan blok tersinkronisasi adalah penguncian ganda diperiksa dalam pola Singleton di mana alih-alih mengunci seluruh getInstance()
metode, kami hanya mengunci bagian kode yang penting yang digunakan untuk membuat instance Singleton. Ini meningkatkan kinerja secara drastis karena penguncian hanya diperlukan satu atau dua kali.
Saat menggunakan metode yang disinkronkan, Anda harus berhati-hati jika Anda mencampur metode tersinkronisasi statis dan non-statis.
monitorenter
dan monitorexit
sebelum menjalankan kode.
Paling sering saya menggunakan ini untuk menyinkronkan akses ke daftar atau peta tetapi saya tidak ingin memblokir akses ke semua metode objek.
Dalam kode berikut satu utas memodifikasi daftar tidak akan memblokir menunggu utas yang mengubah peta. Jika metode disinkronkan pada objek maka setiap metode harus menunggu meskipun modifikasi yang mereka buat tidak akan konflik.
private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();
public void put( String s, Bar b ) {
synchronized( myMap ) {
myMap.put( s,b );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public void hasKey( String s, ) {
synchronized( myMap ) {
myMap.hasKey( s );
}
}
public void add( Foo f ) {
synchronized( myList ) {
myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public Thing getMedianFoo() {
Foo med = null;
synchronized( myList ) {
Collections.sort(myList);
med = myList.get(myList.size()/2);
}
return med;
}
Dengan blok yang disinkronkan, Anda dapat memiliki beberapa sinkronisasi, sehingga banyak hal yang simultan tetapi tidak bertentangan dapat berlangsung secara bersamaan.
Metode yang disinkronkan dapat diperiksa menggunakan API refleksi. Ini berguna untuk menguji beberapa kontrak, seperti semua metode dalam model disinkronkan .
Cuplikan berikut mencetak semua metode Hashtable yang disinkronkan:
for (Method m : Hashtable.class.getMethods()) {
if (Modifier.isSynchronized(m.getModifiers())) {
System.out.println(m);
}
}
Catatan penting tentang penggunaan blok yang disinkronkan: hati-hati apa yang Anda gunakan sebagai objek kunci!
Cuplikan kode dari user2277816 di atas mengilustrasikan hal ini karena referensi ke string literal digunakan sebagai objek penguncian. Sadarilah bahwa string literal secara otomatis diinternir di Jawa dan Anda harus mulai melihat masalahnya: setiap bagian kode yang disinkronkan pada "kunci" literal, berbagi kunci yang sama! Ini dapat dengan mudah menyebabkan kebuntuan dengan potongan kode yang sama sekali tidak terkait.
Bukan hanya objek String yang perlu Anda perhatikan. Kotak primitif juga merupakan bahaya, karena proses otomatis dan metode valueOf dapat menggunakan kembali objek yang sama, tergantung pada nilainya.
Untuk informasi lebih lanjut, lihat: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reusedreused
Seringkali menggunakan kunci pada level metode terlalu kasar. Mengapa mengunci sepotong kode yang tidak mengakses sumber daya bersama dengan mengunci seluruh metode. Karena setiap objek memiliki kunci, Anda dapat membuat objek tiruan untuk menerapkan sinkronisasi level blok. Level blok lebih efisien karena tidak mengunci keseluruhan metode.
Berikut beberapa contohnya
Tingkat Metode
class MethodLevel {
//shared among threads
SharedResource x, y ;
public void synchronized method1() {
//multiple threads can't access
}
public void synchronized method2() {
//multiple threads can't access
}
public void method3() {
//not synchronized
//multiple threads can access
}
}
Tingkat Blok
class BlockLevel {
//shared among threads
SharedResource x, y ;
//dummy objects for locking
Object xLock = new Object();
Object yLock = new Object();
public void method1() {
synchronized(xLock){
//access x here. thread safe
}
//do something here but don't use SharedResource x, y
// because will not be thread-safe
synchronized(xLock) {
synchronized(yLock) {
//access x,y here. thread safe
}
}
//do something here but don't use SharedResource x, y
//because will not be thread-safe
}//end of method1
}
[Sunting]
Untuk Collection
suka Vector
dan Hashtable
mereka disinkronkan saat ArrayList
atau HashMap
tidak dan Anda perlu mengatur kata kunci yang disinkronkan atau memanggil metode yang disinkronkan Koleksi:
Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
Satu-satunya perbedaan: blok tersinkronisasi memungkinkan penguncian granular tidak seperti metode yang disinkronkan
Pada dasarnya synchronized
blok atau metode telah digunakan untuk menulis kode aman utas dengan menghindari kesalahan inkonsistensi memori.
Pertanyaan ini sudah sangat lama dan banyak hal telah berubah selama 7 tahun terakhir. Konstruksi pemrograman baru telah diperkenalkan untuk keamanan utas.
Anda dapat mencapai keamanan utas dengan menggunakan API konkurensi canggih, bukan synchronied
blok. Halaman dokumentasi ini menyediakan konstruksi pemrograman yang baik untuk mencapai keamanan utas.
Lock Objects mendukung idiom pengunci yang menyederhanakan banyak aplikasi bersamaan.
Pelaksana menetapkan API tingkat tinggi untuk meluncurkan dan mengelola utas. Implementasi pelaksana yang disediakan oleh java.util.concurrent menyediakan manajemen kumpulan thread yang cocok untuk aplikasi skala besar.
Koleksi Bersamaan membuatnya lebih mudah untuk mengelola koleksi data yang besar, dan dapat sangat mengurangi kebutuhan untuk sinkronisasi.
Variabel Atom memiliki fitur yang meminimalkan sinkronisasi dan membantu menghindari kesalahan konsistensi memori.
ThreadLocalRandom (dalam JDK 7) menyediakan generasi nomor pseudorandom yang efisien dari berbagai utas.
Penggantian yang lebih baik untuk disinkronkan adalah ReentrantLock , yang menggunakan Lock
API
Pengecualian mutual mutual reentrant dengan perilaku dasar dan semantik yang sama dengan kunci monitor implisit yang diakses menggunakan metode dan pernyataan yang disinkronkan, tetapi dengan kemampuan yang diperluas.
Contoh dengan kunci:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
Mengacu pada java.util.concurrent dan java.util.concurrent.atomic paket juga untuk konstruksi pemrograman lainnya.
Lihat pertanyaan terkait ini juga:
Secara umum ini sebagian besar sama selain eksplisit tentang monitor objek yang digunakan vs objek implisit ini. Salah satu kelemahan dari metode yang disinkronkan yang saya pikir kadang-kadang diabaikan adalah bahwa dalam menggunakan referensi "ini" untuk menyinkronkan Anda membiarkan kemungkinan objek eksternal terkunci pada objek yang sama. Itu bisa menjadi bug yang sangat halus jika Anda menabraknya. Sinkronisasi pada Obyek eksplisit internal atau bidang lain yang ada dapat menghindari masalah ini, sepenuhnya merangkum sinkronisasi.
Seperti yang sudah dikatakan di sini, blok yang disinkronkan dapat menggunakan variabel yang ditentukan pengguna sebagai objek kunci, ketika fungsi yang disinkronkan hanya menggunakan "ini". Dan tentu saja Anda dapat memanipulasi dengan area fungsi Anda yang harus disinkronkan. Tetapi semua orang mengatakan bahwa tidak ada perbedaan antara fungsi yang disinkronkan dan blok yang mencakup seluruh fungsi menggunakan "ini" sebagai objek kunci. Itu tidak benar, perbedaannya adalah dalam kode byte yang akan dihasilkan dalam kedua situasi. Dalam hal penggunaan blok tersinkronisasi harus dialokasikan variabel lokal yang memegang referensi untuk "ini". Dan sebagai hasilnya kita akan memiliki ukuran yang sedikit lebih besar untuk fungsi (tidak relevan jika Anda hanya memiliki sedikit fungsi).
Penjelasan lebih rinci tentang perbedaan ini dapat Anda temukan di sini: http://www.artima.com/insidejvm/ed2/threadsynchP.html
Dalam hal metode yang disinkronkan, kunci akan diperoleh pada Obyek. Tetapi jika Anda pergi dengan blok yang disinkronkan Anda memiliki opsi untuk menentukan objek di mana kunci akan diperoleh.
Contoh:
Class Example {
String test = "abc";
// lock will be acquired on String test object.
synchronized (test) {
// do something
}
lock will be acquired on Example Object
public synchronized void testMethod() {
// do some thing
}
}
Saya tahu ini adalah pertanyaan lama, tetapi dengan cepat saya membaca tanggapan di sini, saya tidak benar-benar melihat ada yang menyebutkan bahwa kadang-kadang synchronized
metode mungkin kunci yang salah .
Dari Java Concurrency In Practice (hal 72):
public class ListHelper<E> {
public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...
public syncrhonized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
Kode di atas memiliki tampilan sebagai thread-safe. Namun, pada kenyataannya tidak. Dalam hal ini kunci diperoleh pada instance kelas. Namun, daftar dapat dimodifikasi oleh utas lain yang tidak menggunakan metode itu. Pendekatan yang benar adalah menggunakan
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
Kode di atas akan memblokir semua utas yang mencoba mengubah daftar dari memodifikasi daftar hingga blok yang disinkronkan selesai.
List
dapat menyebabkan masalah kinerja jika ada log kode yang tidak perlu disinkronkan
Sebagai masalah praktis, keuntungan dari metode yang disinkronkan dari blok yang disinkronkan adalah bahwa mereka lebih tahan terhadap idiot; karena Anda tidak dapat memilih objek sewenang-wenang untuk dikunci, Anda tidak dapat menyalahgunakan sintaksis metode yang disinkronkan untuk melakukan hal-hal bodoh seperti mengunci string literal atau mengunci konten bidang yang bisa berubah yang diganti dari bawah utas.
Di sisi lain, dengan metode yang disinkronkan Anda tidak dapat melindungi kunci agar tidak didapat oleh utas apa pun yang bisa mendapatkan referensi ke objek.
Jadi menggunakan sinkronisasi sebagai pengubah pada metode lebih baik dalam melindungi pekerja sapi Anda dari menyakiti diri mereka sendiri, sementara menggunakan blok tersinkronisasi dalam hubungannya dengan objek kunci pribadi akhir lebih baik melindungi kode Anda sendiri dari pekerja sapi.
Dari ringkasan spesifikasi Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html
Pernyataan yang disinkronkan (§14.17) menghitung referensi ke objek; kemudian mencoba melakukan tindakan kunci pada objek itu dan tidak melanjutkan lebih jauh sampai tindakan kunci berhasil diselesaikan. ...
Metode yang disinkronkan (§8.4.3.5) secara otomatis melakukan aksi kunci ketika dipanggil; tubuhnya tidak dieksekusi sampai tindakan kunci berhasil diselesaikan. Jika metode ini adalah metode instance , itu mengunci kunci yang terkait dengan instance yang dipanggil (yaitu, objek yang akan dikenal sebagai ini selama eksekusi dari tubuh metode). Jika metode ini statis , ia mengunci kunci yang terkait dengan objek Kelas yang mewakili kelas di mana metode ini didefinisikan. ...
Berdasarkan uraian ini, saya akan mengatakan sebagian besar jawaban sebelumnya benar, dan metode yang disinkronkan mungkin sangat berguna untuk metode statis, di mana Anda harus mencari cara untuk mendapatkan "objek Class yang mewakili kelas di mana metode itu didefinisikan. "
Sunting: Awalnya saya pikir ini adalah kutipan dari spesifikasi Java yang sebenarnya. Mengklarifikasi bahwa halaman ini hanyalah ringkasan / penjelasan dari spesifikasi
TLDR; Tidak menggunakan synchronized
pengubah atau synchronized(this){...}
ekspresi tetapi di synchronized(myLock){...}
mana myLock
bidang contoh terakhir memegang objek pribadi.
Perbedaan antara menggunakan synchronized
pengubah pada deklarasi metode dan synchronized(..){ }
ekspresi dalam tubuh metode adalah ini:
synchronized
pengubah ditentukan pada tanda tangan metode ini
synchronized(this) { .... }
, danthis
objek sebagai kunci ketika dideklarasikan pada metode non-statis atau kelas penutup ketika dideklarasikan pada metode statis.synchronized(...){...}
ekspresi memungkinkan Anda
Namun, menggunakan synchronized
pengubah atau synchronized(...) {...}
dengan this
sebagai objek kunci (seperti dalam synchronized(this) {...}
), memiliki kelemahan yang sama. Keduanya menggunakan instance itu sendiri sebagai objek kunci untuk disinkronkan. Hal ini berbahaya karena tidak hanya obyek itu sendiri tetapi setiap eksternal lainnya objek / kode yang memegang referensi ke objek yang juga dapat menggunakannya sebagai kunci sinkronisasi dengan efek samping yang berat (penurunan kinerja dan deadlock ).
Oleh karena itu praktik terbaik adalah tidak menggunakan synchronized
pengubah atau synchronized(...)
ekspresi dalam hubungannya dengan this
sebagai objek kunci tetapi objek kunci pribadi untuk objek ini. Sebagai contoh:
public class MyService {
private final lock = new Object();
public void doThis() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
public void doThat() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
}
Anda juga dapat menggunakan beberapa objek kunci tetapi perlu perhatian khusus untuk memastikan ini tidak menghasilkan kebuntuan saat digunakan bersarang.
public class MyService {
private final lock1 = new Object();
private final lock2 = new Object();
public void doThis() {
synchronized(lock1) {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThat() and doMore().
}
}
public void doThat() {
synchronized(lock1) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doMore() may execute concurrently
}
}
public void doMore() {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doThat() may execute concurrently
}
}
}
Saya kira pertanyaan ini adalah tentang perbedaan antara Thread Safe Singleton dan inisialisasi Lazy dengan penguncian cek ganda . Saya selalu merujuk ke artikel ini ketika saya perlu menerapkan beberapa singleton spesifik.
Nah, ini adalah Singleton Safe Thread :
// Java program to create Thread Safe
// Singleton class
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
//synchronized method to control simultaneous access
synchronized public static GFG getInstance()
{
if (instance == null)
{
// if instance is null, initialize
instance = new GFG();
}
return instance;
}
}
Pro:
Inisialisasi malas dimungkinkan.
Itu aman dari utas.
Cons:
- Metode getInstance () disinkronkan sehingga menyebabkan kinerja lambat karena beberapa utas tidak dapat mengaksesnya secara bersamaan.
Ini adalah inisialisasi Malas dengan penguncian periksa ganda :
// Java code to explain double check locking
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
public static GFG getInstance()
{
if (instance == null)
{
//synchronized block to remove overhead
synchronized (GFG.class)
{
if(instance==null)
{
// if instance is null, initialize
instance = new GFG();
}
}
}
return instance;
}
}
Pro:
Inisialisasi malas dimungkinkan.
Ini juga aman.
Performa berkurang karena kata kunci yang disinkronkan diatasi.
Cons:
Pertama kali, itu dapat mempengaruhi kinerja.
Sebagai kontra. metode penguncian periksa ganda tertahankan sehingga dapat digunakan untuk aplikasi multi-ulir berkinerja tinggi.
Silakan merujuk ke artikel ini untuk lebih jelasnya:
https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/
Sinkronisasi dengan utas. 1) JANGAN PERNAH menggunakan disinkronkan (ini) di utas itu tidak berfungsi. Sinkronisasi dengan (ini) menggunakan utas saat ini sebagai objek utas pengunci. Karena setiap utas tidak tergantung pada utas lainnya, tidak ada koordinasi sinkronisasi. 2) Pengujian kode menunjukkan bahwa pada Java 1.6 pada Mac metode sinkronisasi tidak berfungsi. 3) disinkronkan (lockObj) di mana lockObj adalah objek bersama dari semua utas yang disinkronkan akan berfungsi. 4) ReenterantLock.lock () dan .unlock () berfungsi. Lihat tutorial Java untuk ini.
Kode berikut menunjukkan poin-poin ini. Ini juga mengandung Vector thread-safe yang akan diganti untuk ArrayList, untuk menunjukkan bahwa banyak utas yang menambahkan ke Vector tidak kehilangan informasi apa pun, sedangkan yang sama dengan ArrayList dapat kehilangan informasi. 0) Kode saat ini menunjukkan hilangnya informasi karena kondisi balapan A) Komentari garis berlabel A saat ini, dan batalkan komentar pada garis A di atasnya, lalu jalankan, metode kehilangan data tetapi tidak seharusnya. B) Balikkan langkah A, tanda komentar B dan // blok akhir}. Kemudian jalankan untuk melihat hasil, tidak ada kehilangan data C) Komentar B, batalkan komentar C. Jalankan, lihat sinkronisasi pada (ini) kehilangan data, seperti yang diharapkan. Tidak punya waktu untuk menyelesaikan semua variasi, harap ini membantu. Jika sinkronisasi pada (ini), atau metode sinkronisasi berfungsi, sebutkan versi Java dan OS apa yang Anda uji. Terima kasih.
import java.util.*;
/** RaceCondition - Shows that when multiple threads compete for resources
thread one may grab the resource expecting to update a particular
area but is removed from the CPU before finishing. Thread one still
points to that resource. Then thread two grabs that resource and
completes the update. Then thread one gets to complete the update,
which over writes thread two's work.
DEMO: 1) Run as is - see missing counts from race condition, Run severa times, values change
2) Uncomment "synchronized(countLock){ }" - see counts work
Synchronized creates a lock on that block of code, no other threads can
execute code within a block that another thread has a lock.
3) Comment ArrayList, unComment Vector - See no loss in collection
Vectors work like ArrayList, but Vectors are "Thread Safe"
May use this code as long as attribution to the author remains intact.
/mf
*/
public class RaceCondition {
private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
// private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)
private String countLock="lock"; // Object use for locking the raceCount
private int raceCount = 0; // simple add 1 to this counter
private int MAX = 10000; // Do this 10,000 times
private int NUM_THREADS = 100; // Create 100 threads
public static void main(String [] args) {
new RaceCondition();
}
public RaceCondition() {
ArrayList<Thread> arT = new ArrayList<Thread>();
// Create thread objects, add them to an array list
for( int i=0; i<NUM_THREADS; i++){
Thread rt = new RaceThread( ); // i );
arT.add( rt );
}
// Start all object at once.
for( Thread rt : arT ){
rt.start();
}
// Wait for all threads to finish before we can print totals created by threads
for( int i=0; i<NUM_THREADS; i++){
try { arT.get(i).join(); }
catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
}
// All threads finished, print the summary information.
// (Try to print this informaiton without the join loop above)
System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
MAX*NUM_THREADS, raceList.size(), raceCount );
System.out.printf("Array lost %,d. Count lost %,d\n",
MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
} // end RaceCondition constructor
class RaceThread extends Thread {
public void run() {
for ( int i=0; i<MAX; i++){
try {
update( i );
} // These catches show when one thread steps on another's values
catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
catch( OutOfMemoryError oome ) { System.out.print("O"); }
}
}
// so we don't lose counts, need to synchronize on some object, not primitive
// Created "countLock" to show how this can work.
// Comment out the synchronized and ending {, see that we lose counts.
// public synchronized void update(int i){ // use A
public void update(int i){ // remove this when adding A
// synchronized(countLock){ // or B
// synchronized(this){ // or C
raceCount = raceCount + 1;
raceList.add( i ); // use Vector
// } // end block for B or C
} // end update
} // end RaceThread inner class
} // end RaceCondition outter class