Jika saya menyinkronkan dua metode pada kelas yang sama, dapatkah mereka berjalan secara bersamaan?

164

Jika saya menyinkronkan dua metode pada kelas yang sama, dapatkah mereka berjalan secara bersamaan pada objek yang sama ? sebagai contoh:

class A {
    public synchronized void methodA() {
        //method A
    }

    public synchronized void methodB() {
        // method B
    }
}

Saya tahu bahwa saya tidak dapat menjalankan methodA()dua kali pada objek yang sama di dua utas yang berbeda. hal yang sama di methodB().

Tetapi bisakah saya menjalankan methodB()thread yang berbeda saat methodA()masih berjalan? (objek yang sama)

Shelef
sumber

Jawaban:

148

Kedua metode mengunci monitor yang sama. Oleh karena itu, Anda tidak dapat secara bersamaan menjalankannya pada objek yang sama dari utas yang berbeda (salah satu dari dua metode akan diblokir sampai yang lainnya selesai).

NPE
sumber
1
Saya menambahkan pertanyaan ini. Misalkan kedua metode statis sekarang methodA disebut menggunakan Class sedangkan methodB disebut menggunakan objek seperti A.methodA () di t1 dan obj.methodB () di t2. Apa yang akan terjadi sekarang, akankah mereka memblokir ????
amod
2
@ amod0017: obj.methodB()adalah sinonim untuk A.methodB()saat methodB()ini static. Karena itu ya, mereka akan memblokir (pada kelas, bukan objek, monitor).
NPE
akan mencoba dan kembali ke sana. :)
amod
@NPE Jadi walaupun kedua metode ini statis dan 2 utas t1 dan t2 pada objek yang sama mencoba memanggil methodA () dan methodB () secara bersamaan maka hanya 1 (misalkan t1) utas yang akan dijalankan dan utas lainnya harus menunggu hingga t1 melepaskan kunci ?
sreeprasad
8
Perlu diingat bahwa metode statis menggunakan kunci pada .classobjek. Jadi kalau sudah class A {static synchronized void m() {} }. Dan kemudian satu utas menyebutnya new A().m()memperoleh kunci pada new A()objek. Jika kemudian thread lain memanggil A.m()itu memasuki METODE NO PROBLEM karena apa yang tampak adalah kunci pada A.classobjek sementara NO THREADS memiliki semacam ini kunci . Jadi meskipun Anda menyatakan metode synchronizeditu sebenarnya diakses oleh dua utas yang berbeda PADA SAAT YANG SAMA . Jadi: jangan pernah menggunakan referensi objek untuk memanggil metode statis
Alex Semeniuk
113

Dalam contoh methodA dan methodB adalah metode instance (sebagai lawan dari metode statis). Menempatkan synchronizedmetode instance berarti bahwa utas harus mendapatkan kunci ("kunci intrinsik") pada objek contoh di mana metode dipanggil sebelum utas dapat mulai mengeksekusi kode apa pun dalam metode itu.

Jika Anda memiliki dua metode instance berbeda yang ditandai tersinkronisasi dan utas berbeda memanggil metode-metode tersebut secara bersamaan pada objek yang sama, utas tersebut akan bersaing untuk kunci yang sama. Setelah satu utas mendapatkan kunci, semua utas lainnya akan ditutup dari semua metode instance yang disinkronkan pada objek itu.

Agar kedua metode dapat berjalan secara bersamaan, mereka harus menggunakan kunci yang berbeda, seperti ini:

class A {
    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void methodA() {
        synchronized(lockA) {
            //method A
        }
    }

    public void methodB() {
        synchronized(lockB) {
            //method B
        }
    }
}

di mana sintaksis blok yang disinkronkan memungkinkan menentukan objek tertentu yang perlu dijalankan utas pelaksana untuk mendapatkan kunci intrinsik agar dapat memasuki blok.

Yang penting untuk dipahami adalah bahwa meskipun kami menempatkan kata kunci "tersinkronisasi" pada metode individual, konsep intinya adalah kunci intrinsik di belakang layar.

Berikut adalah cara tutorial Java menjelaskan hubungan:

Sinkronisasi dibangun di sekitar entitas internal yang dikenal sebagai kunci intrinsik atau kunci monitor. (Spesifikasi API sering merujuk pada 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 membutuhkan 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.

Tujuan dari penguncian adalah untuk melindungi data yang dibagikan. Anda akan menggunakan kunci terpisah seperti yang ditunjukkan dalam kode contoh di atas hanya jika setiap kunci melindungi anggota data yang berbeda.

Nathan Hughes
sumber
jadi dalam contoh ini kuncinya ada pada objek lockA \ lockB dan bukan pada kelas A? Apakah ini contoh penguncian tingkat kelas ?
Nimrod
2
@ Nimrod: itu mengunci pada lockA dan pada objek lockB dan bukan pada instance A. tidak ada yang mengunci pada kelas. penguncian tingkat kelas akan berarti mendapatkan kunci pada objek kelas, menggunakan sesuatu seperti static synchronizedatausynchronized (A.class)
Nathan Hughes
Berikut ini tautan ke tutorial java yang menjelaskan dengan tepat apa yang dijawab di sini.
Alberto de Paola
18

Java Thread mengakuisisi sebuah kunci tingkat objek ketika memasuki sebuah contoh metode java disinkronkan dan mengakuisisi kunci tingkat kelas ketika memasuki statis disinkronisasi metode java.

Dalam kasus Anda, metode (contoh) dari kelas yang sama. Jadi kapan pun sebuah thread masuk ke metode java disinkronkan atau blok itu memperoleh kunci (objek tempat metode dipanggil). Jadi metode lain tidak dapat dipanggil pada saat yang sama pada objek yang sama sampai metode pertama selesai dan kunci (objek) dilepaskan.

Srikanth
sumber
jika saya memiliki dua utas pada dua instance kelas yang berbeda maka mereka akan dapat menjalankan kedua metode secara bersamaan sehingga satu utas memanggil satu metode yang disinkronkan dan yang lainnya memanggil metode kedua yang disinkronkan. Jika pemahaman saya benar maka dapatkah saya menggunakan private final Object lock = new object();dengan disinkronkan untuk memungkinkan hanya satu utas untuk dapat menjalankan salah satu metode? Terima kasih
Yug Singh
13

Dalam kasus Anda, Anda menyinkronkan dua metode pada instance kelas yang sama. Jadi, kedua metode ini tidak dapat berjalan secara bersamaan pada utas berbeda dari instance yang sama dari kelas A. Tapi mereka bisa pada instance kelas A yang berbeda.

class A {
    public synchronized void methodA() {
        //method A
    }
}

sama dengan:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}
Oleksandr_DJ
sumber
bagaimana jika saya mendefinisikan kunci sebagai private final Object lock = new Object();dan sekarang digunakan lockdengan blok disinkronkan dalam dua metode maka akankah pernyataan Anda berlaku? IMO karena Object adalah kelas induk dari semua objek sehingga bahkan jika utas berada pada instance kelas yang berbeda, hanya satu yang dapat mengakses kode di dalam blok yang disinkronkan pada suatu waktu. Terima kasih.
Yug Singh
Jika Anda mendefinisikan "kunci objek akhir pribadi" di kelas dan menyinkronkan dengan itu, Anda mengisi memiliki kunci per instance kelas, jadi, itu akan berperilaku sama dengan disinkronkan (ini).
Oleksandr_DJ
Ya, Obyek adalah induk untuk semua kelas, tetapi "kunci" contoh dalam kasus Anda adalah "instance per memiliki kelas", jadi, ia memiliki efek yang sama dengan "ini" untuk sinkronisasi.
Oleksandr_DJ
7

Dari tautan dokumentasi oracle

Membuat metode yang disinkronkan memiliki dua efek:

Pertama, tidak mungkin untuk dua doa metode disinkronkan pada objek yang sama untuk interleave. Ketika satu utas menjalankan metode yang disinkronkan untuk suatu objek, semua utas lainnya yang menjalankan metode yang disinkronkan untuk blok objek yang sama (menunda eksekusi) hingga utas pertama selesai dengan objek tersebut.

Kedua, ketika metode yang disinkronkan keluar, itu secara otomatis membangun hubungan yang terjadi sebelum dengan doa berikutnya dari metode yang disinkronkan untuk objek yang sama. Ini menjamin bahwa perubahan keadaan objek terlihat oleh semua utas

Ini akan menjawab pertanyaan Anda: Pada objek yang sama, Anda tidak dapat memanggil metode tersinkronisasi kedua saat eksekusi metode tersinkronisasi pertama sedang berlangsung.

Lihat halaman dokumentasi ini untuk memahami kunci intrinsik dan perilaku kunci.

Aditya W
sumber
6

Pikirkan kode Anda seperti di bawah ini:

class A {

public void methodA() {
    synchronized(this){        
      //method A body
    }
}

public void methodB() {
    synchronized(this){
      // method B body
    }
}

Jadi, disinkronkan pada level metode berarti disinkronkan (ini). jika ada thread menjalankan metode kelas ini, itu akan mendapatkan kunci sebelum memulai eksekusi dan tahan sampai eksekusi metode selesai.

Tapi bisakah saya menjalankan methodB () pada utas yang berbeda sementara methodA () masih berjalan? (objek yang sama)

Memang tidak mungkin!

Oleh karena itu, banyak utas tidak dapat menjalankan sejumlah metode yang disinkronkan pada objek yang sama secara bersamaan.

Khosro Makari
sumber
bagaimana jika saya membuat Utas pada dua objek berbeda dari kelas yang sama? Dalam hal ini Jika saya memanggil satu metode dari satu utas dan metode lain dari utas kedua tidak akankah mereka menjalankan secara bersamaan?
Yug Singh
2
Mereka akan melakukannya karena mereka adalah objek yang berbeda. Artinya, jika Anda ingin mencegahnya, Anda bisa menggunakan metode statis dan menyinkronkan kelas atau menggunakan objek variabel kelas sebagai kunci atau membuat kelas Singleton. @Yug Singh
Khosro Makari
4

Hanya untuk semua kejelasan, Ada kemungkinan bahwa kedua metode disinkronkan statis dan tidak disinkronkan dapat berjalan secara bersamaan atau bersamaan karena seseorang memiliki kunci tingkat objek dan kunci tingkat kelas lainnya.

pacmanfordinner
sumber
3

The key ide dengan sinkronisasi yang tidak meresap dengan mudah adalah bahwa hal itu hanya akan berpengaruh jika metode yang disebut pada objek yang sama misalnya - itu telah disorot dalam jawaban dan komentar -

Program sampel di bawah ini adalah dengan jelas menunjukkan hal yang sama -

public class Test {

public synchronized void methodA(String currentObjectName) throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA out");
}

public synchronized void methodB(String currentObjectName)  throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB out");
}

public static void main(String[] args){
    Test object1 = new Test();
    Test object2 = new Test();
    //passing object instances to the runnable to make calls later
    TestRunner runner = new TestRunner(object1,object2);
    // you need to start atleast two threads to properly see the behaviour
    Thread thread1 = new Thread(runner);
    thread1.start();
    Thread thread2 = new Thread(runner);
    thread2.start();
}
}

class TestRunner implements Runnable {
Test object1;
Test object2;

public TestRunner(Test h1,Test h2) {
    this.object1 = h1;
    this.object2 = h2;
}

@Override
public void run() {
    synchronizedEffectiveAsMethodsCalledOnSameObject(object1);
    //noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(object1,object2);
}

// this method calls the method A and B with same object instance object1 hence simultaneous NOT possible
private void synchronizedEffectiveAsMethodsCalledOnSameObject(Test object1) {
    try {
        object1.methodA("object1");
        object1.methodB("object1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

// this method calls the method A and B with different object instances object1 and object2 hence simultaneous IS possible
private void noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(Test object1,Test object2) {
    try {
        object1.methodA("object1");
        object2.methodB("object2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

Perhatikan perbedaan dalam output tentang bagaimana akses simultan diizinkan seperti yang diharapkan jika metode dipanggil pada instance objek yang berbeda.

Ouput dengan noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () berkomentar - outputnya dalam metode urutanA di> methodA Out .. methodB in> methodB Out Ouput dengan * noEffectOfSinkronkanAsMethodsCalledOnDifferentObjects () * berkomentar

dan Ouput dengan sinkronisasiEffectiveAsMethodsCalledOnSameObject () berkomentar - output menunjukkan akses simultan metodeA oleh Thread1 dan Thread0 di bagian yang disorot -

Ouput dengan * disinkronkanEfektifAsMetodeCalledOnSameObject () * berkomentar

Menambah jumlah utas akan membuatnya semakin terlihat.

somshivam
sumber
2

Tidak itu tidak mungkin, jika memungkinkan maka kedua metode dapat memperbarui variabel yang sama secara bersamaan yang dapat dengan mudah merusak data.

fastcodejava
sumber
2

Ya, mereka dapat menjalankan kedua utas secara bersamaan. Jika Anda membuat 2 objek kelas karena setiap objek hanya berisi satu kunci dan setiap metode yang disinkronkan memerlukan kunci. Jadi jika Anda ingin menjalankan secara bersamaan, buat dua objek lalu coba jalankan dengan menggunakan referensi objek tersebut.

mendingin
sumber
1

Anda menyinkronkannya pada objek bukan pada kelas. Jadi mereka tidak bisa berjalan secara bersamaan pada objek yang sama

xyz
sumber
0

Dua Thread berbeda yang mengeksekusi metode yang disinkronkan umum pada objek tunggal, karena objeknya sama, ketika satu thread menggunakannya dengan metode yang disinkronkan, itu harus memvariasikan kunci, jika kunci diaktifkan, utas ini akan menunggu status, jika kunci dinonaktifkan maka ia dapat mengakses objek, sementara itu akan mengaksesnya akan mengaktifkan kunci dan akan melepaskan kunci hanya ketika eksekusi selesai. ketika utas lainnya tiba, itu akan memvariasikan kunci, karena diaktifkan itu akan menunggu sampai utas pertama menyelesaikan eksekusi dan melepaskan kunci yang diletakkan pada objek, setelah kunci dilepaskan utas kedua akan mendapatkan akses ke objek dan itu akan mengaktifkan kunci sampai pelaksanaannya. jadi eksekusi tidak akan bersamaan, kedua utas akan mengeksekusi satu per satu,

Ankit yadav
sumber
1
Harap beri tanda baca dan gunakan huruf besar untuk kekacauan ini dengan benar. Tidak ada kata 'varify'.
Marquis of Lorne