Dalam bab ini, kami akan memperkenalkan kata kunci yang disinkronkan. Konten yang terlibat meliputi:
1. Prinsip yang disinkronkan
2. Aturan Dasar Sinkronisasi
3. Metode yang disinkronkan dan blok kode yang disinkronkan
4. Kunci instan dan kunci global
1. Prinsip yang disinkronkan
Di Java, setiap objek memiliki dan hanya memiliki satu kunci sinkronisasi. Ini juga berarti bahwa kunci sinkronisasi ada pada objek.
Ketika kami memanggil metode sinkronisasi suatu objek, kami memperoleh kunci sinkronisasi objek. Misalnya, disinkronkan (OBJ) memperoleh kunci sinkronisasi "objek OBJ".
Akses ke kunci sinkronisasi oleh utas yang berbeda saling eksklusif. Dengan kata lain, pada titik waktu tertentu, kunci sinkronisasi objek hanya dapat diperoleh dengan satu utas! Melalui kunci sinkronisasi, kita dapat mencapai akses yang saling eksklusif ke "objek/metode" di banyak utas. Misalnya, sekarang ada dua utas A dan Thread B, yang keduanya mengakses "kunci sinkron objek OBJ". Misalkan pada titik tertentu, utas A memperoleh "kunci sinkronisasi OBJ" dan melakukan beberapa operasi; B hanya dapat memperoleh "Kunci Sinkronisasi OBJ" sampai utas A melepaskan "kunci sinkron objek ini" dan hanya dapat berjalan.
2. Aturan Dasar Sinkronisasi
Kami merangkum aturan dasar yang disinkronkan ke dalam 3 berikut dan menggambarkannya melalui contoh.
Pasal 1: Ketika utas mengakses "metode yang disinkronkan" atau "blok kode yang disinkronkan" dari "objek tertentu", utas lain akan diblokir dari akses ke "metode sinkronisasi" atau "blok kode yang disinkronkan" dari "objek".
Pasal 2: Ketika utas mengakses "metode yang disinkronkan" atau "blok kode yang disinkronkan" dari "objek tertentu", utas lain masih dapat mengakses blok kode yang disinkronkan dari "objek ini".
Pasal 3: Ketika utas mengakses "metode yang disinkronkan" atau "blok kode yang disinkronkan" dari "objek tertentu", utas lain akan diblokir dari mengakses "metode sinkronisasi" atau "blok kode yang disinkronkan" lainnya dari "objek".
Pasal 1
Ketika utas mengakses "metode sinkronisasi" atau "blok kode yang disinkronkan" dari "objek tertentu", utas lain akan diblokir dari akses ke "metode yang disinkronkan" atau "blok kode yang disinkronkan" dari "objek".
Di bawah ini adalah program demonstrasi yang sesuai dengan "blok kode yang disinkronkan".
Salinan kode adalah sebagai berikut:
kelas myrunable mengimplementasikan runnable {
@Mengesampingkan
public void run () {
disinkronkan (ini) {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName () + "loop" + i);
}
} catch (InterruptedException IE) {
}
}
}
}
kelas publik demo1_1 {
public static void main (string [] args) {
Demo Runnable = MyRunable baru (); // Buat "Objek Runnable" baru
Thread T1 = Thread Baru (Demo, "T1");
Thread T2 = Thread Baru (Demo, "T2");
t1.start (); // Mulai "Thread T1"
t2.start (); // Mulai "Thread T2"
}
}
Hasil Menjalankan:
Salinan kode adalah sebagai berikut:
Loop T1 0
Loop T1 1
Loop T1 2
Loop T1 3
Loop T1 4
Loop T2 0
Loop T2 1
T2 Loop 2
Loop T2 3
Loop T2 4
Deskripsi Hasil:
Ada "blok kode yang disinkronkan (ini)" dalam metode run (), dan T1 dan T2 adalah utas yang dibuat berdasarkan "demo" objek yang dapat dijalankan. Ini berarti bahwa kita dapat menganggap ini secara disinkronkan (ini) sebagai "objek yang dapat dijalankan"; Oleh karena itu, ketika satu utas berjalan, utas lain harus menunggu "utas berjalan" untuk melepaskan "kunci sinkronisasi demo" sebelum dapat berjalan.
Jika Anda mengonfirmasi, Anda menemukan masalah ini. Kemudian kami memodifikasi kode di atas, dan kemudian menjalankannya untuk melihat bagaimana hasilnya, dan melihat apakah Anda akan bingung. Kode sumber yang dimodifikasi adalah sebagai berikut:
Salinan kode adalah sebagai berikut:
kelas mythread memperluas utas {
mythread publik (nama string) {
super (nama);
}
@Mengesampingkan
public void run () {
disinkronkan (ini) {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName () + "loop" + i);
}
} catch (InterruptedException IE) {
}
}
}
}
kelas publik demo1_2 {
public static void main (string [] args) {
Thread t1 = mythread baru ("t1");
Thread t2 = mythread baru ("t2");
t1.start (); // Mulai "Thread T1"
t2.start (); // Mulai "Thread T2"
}
}
Deskripsi Kode:
Membandingkan Demo1_2 dan Demo1_1, kami menemukan bahwa kelas MyThread di Demo1_2 secara langsung diwariskan dari utas, dan T1 dan T2 keduanya adalah utas anak Mythread.
Untungnya, metode "run () dari demo1_2" juga disebut disinkronkan (ini), seperti halnya metode "run () dari demo1_1" juga disebut disinkronkan (ini)!
Jadi, apakah proses eksekusi demo1_2 sama dengan demo1_1?
Hasil Menjalankan:
Salinan kode adalah sebagai berikut:
Loop T1 0
Loop T2 0
Loop T1 1
Loop T2 1
Loop T1 2
T2 Loop 2
Loop T1 3
Loop T2 3
Loop T1 4
Loop T2 4
Deskripsi Hasil:
Jika hasil ini tidak mengejutkan Anda sama sekali, maka saya percaya bahwa Anda memiliki pemahaman yang lebih dalam tentang sinkronisasi dan ini. Jika tidak, silakan lanjutkan membaca analisis di sini.
Ini dalam sinkronisasi (ini) mengacu pada "objek kelas saat ini", yaitu objek saat ini yang sesuai dengan kelas di mana disinkronkan (ini) berada. Tujuannya adalah untuk mendapatkan "kunci sinkron dari objek saat ini".
Untuk Demo1_2, ini disinkronkan (ini) mewakili objek Mythread, sedangkan T1 dan T2 adalah dua objek Mythread yang berbeda. Untuk pasangan Demo1_1, ini disinkronkan (ini) mewakili objek yang dapat dipenuhi;
Pasal 2
Ketika utas mengakses "metode yang disinkronkan" atau "blok kode yang disinkronkan" dari "objek tertentu", utas lain masih dapat mengakses blok kode yang disinkronkan dari "objek ini".
Di bawah ini adalah program demonstrasi yang sesuai dengan "blok kode yang disinkronkan".
Salinan kode adalah sebagai berikut:
Count kelas {
// Metode yang mengandung blok sinkronisasi sinkronisasi
public void synmethod () {
disinkronkan (ini) {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName () + "Synmethod loop" + i);
}
} catch (InterruptedException IE) {
}
}
}
// Metode asinkron
public void nonsynmethod () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName () + "loop nonsynmethod" + i);
}
} catch (InterruptedException IE) {
}
}
}
kelas publik demo2 {
public static void main (string [] args) {
Hitungan Hitung Akhir = Jumlah Baru ();
// Buat T1 baru, T1 akan memanggil metode Synmethod () dari "Objek Hitung"
Utas t1 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
count.synmethod ();
}
}, "T1");
// Buat T2 baru, T2 akan memanggil metode nonsynmethod () dari "Objek Hitung"
Utas t2 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
count.nonsynmethod ();
}
}, "T2");
t1.start (); // Mulai T1
t2.start (); // Mulai T2
}
}
Hasil Menjalankan:
Salinan kode adalah sebagai berikut:
T1 Synmethod Loop 0
T2 Nonsynmethod Loop 0
T1 Synmethod Loop 1
T2 Nonsynmethod Loop 1
T1 Synmethod Loop 2
T2 Nonsynmethod Loop 2
T1 Synmethod Loop 3
T2 Nonsynmethod Loop 3
T1 Synmethod Loop 4
T2 Nonsynmethod Loop 4
Deskripsi Hasil:
Dua utas anak baru T1 dan T2 dibuat di utas utama. T1 akan memanggil metode synmethod () dari objek Count, yang berisi blok sinkronisasi; Ketika T1 berjalan, meskipun disinkronkan (ini) dipanggil untuk mendapatkan "kunci sinkronisasi jumlah"; itu tidak menyebabkan T2 memblokir karena T2 tidak menggunakan kunci sinkronisasi "hitungan".
Pasal 3
Ketika utas mengakses "metode sinkronisasi" atau "blok kode yang disinkronkan" dari "objek tertentu", akses utas lain ke "metode sinkronisasi" lainnya atau "blok kode yang disinkronkan" dari "objek" akan diblokir.
Kami juga akan memodifikasi badan metode nonsynmethod () dalam contoh di atas dengan disinkronkan (ini). Kode sumber yang dimodifikasi adalah sebagai berikut:
Salinan kode adalah sebagai berikut:
Count kelas {
// Metode yang mengandung blok sinkronisasi sinkronisasi
public void synmethod () {
disinkronkan (ini) {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName () + "Synmethod loop" + i);
}
} catch (InterruptedException IE) {
}
}
}
// Juga berisi metode blok sinkronisasi sinkronisasi
public void nonsynmethod () {
disinkronkan (ini) {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName () + "loop nonsynmethod" + i);
}
} catch (InterruptedException IE) {
}
}
}
}
kelas publik demo3 {
public static void main (string [] args) {
Hitungan Hitung Akhir = Jumlah Baru ();
// Buat T1 baru, T1 akan memanggil metode Synmethod () dari "Objek Hitung"
Utas t1 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
count.synmethod ();
}
}, "T1");
// Buat T2 baru, T2 akan memanggil metode nonsynmethod () dari "Objek Hitung"
Utas t2 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
count.nonsynmethod ();
}
}, "T2");
t1.start (); // Mulai T1
t2.start (); // Mulai T2
}
}
(Satu kali) Hasil Eksekusi:
Salinan kode adalah sebagai berikut:
Synmethod (): 11
Synblock (): 3
4. Kunci instan dan kunci global
Kunci instan-terkunci pada objek instan. Jika kelas adalah singleton, maka kunci juga memiliki konsep kunci global.
Kata kunci yang disinkronkan sesuai dengan kunci instance.
Global Lock-- Kunci ini ditargetkan di kelas.
Kunci global sesuai dengan sinkronisasi statis (atau terkunci pada kelas atau objek ClassLoader dari kelas ini).
Ada contoh yang sangat jelas dari "kunci instance" dan "Global Lock":
Salinan kode adalah sebagai berikut:
Kelas PULBIC Something {
public disinkronkan void issynca () {}
public disinkronkan void issyncb () {}
public static static void csynca () {}
public static static void csyncb () {}
}
Misalkan, ada sesuatu yang memiliki dua contoh x dan y. Analisis kunci yang diperoleh dengan empat set ekspresi berikut.
(01) x.issynca () dan x.issyncb ()
(02) x.issynca () dan y.issynca ()
(03) x.csynca () dan y.csyncb ()
(04) x.issynca () dan sesuatu.csynca ()
(01) tidak dapat diakses secara bersamaan. Karena issynca () dan issyncb () keduanya kunci sinkronisasi yang mengakses objek yang sama (objek X)!
Salinan kode adalah sebagai berikut:
// Kode Sumber Locktest2.java
kelas sesuatu {
public disinkronkan void issynca () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": issynca");
}
} catch (InterruptedException IE) {
}
}
public disinkronkan void issyncb () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": issyncb");
}
} catch (InterruptedException IE) {
}
}
public static static void csynca () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": csynca");
}
} catch (InterruptedException IE) {
}
}
public static static void csyncb () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": csyncb");
}
} catch (InterruptedException IE) {
}
}
}
LockTest2 kelas publik {
Something x = new Something ();
Sesuatu y = new sesuatu ();
// bandingkan (02) x.issynca () dengan y.issynca ()
private void test2 () {
// Buat T21 baru, dan T21 akan menghubungi x.issynca ()
Utas t21 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
x.issynca ();
}
}, "T21");
// Buat T22 baru, dan T22 akan menghubungi x.issyncb ()
Utas t22 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
y.issynca ();
}
}, "T22");
t21.start ();
t22.start (); // Mulai T22
}
public static void main (string [] args) {
Locktest2 demo = LockTest2 baru ();
demo.test2 ();
}
}
Hasil Menjalankan:
Salinan kode adalah sebagai berikut:
T11: Issynca
T11: Issynca
T11: Issynca
T11: Issynca
T11: Issynca
T12: ISSYNCB
T12: ISSYNCB
T12: ISSYNCB
T12: ISSYNCB
T12: ISSYNCB
(02) dapat diakses secara bersamaan. Karena tidak mengakses kunci sinkronisasi dari objek yang sama, x.issynca () mengakses kunci sinkronisasi X, sementara y.issynca () mengakses kunci sinkronisasi Y.
Salinan kode adalah sebagai berikut:
// Kode Sumber Locktest2.java
kelas sesuatu {
public disinkronkan void issynca () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": issynca");
}
} catch (InterruptedException IE) {
}
}
public disinkronkan void issyncb () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": issyncb");
}
} catch (InterruptedException IE) {
}
}
public static static void csynca () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": csynca");
}
} catch (InterruptedException IE) {
}
}
public static static void csyncb () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": csyncb");
}
} catch (InterruptedException IE) {
}
}
}
LockTest2 kelas publik {
Something x = new Something ();
Sesuatu y = new sesuatu ();
// bandingkan (02) x.issynca () dengan y.issynca ()
private void test2 () {
// Buat T21 baru, dan T21 akan menghubungi x.issynca ()
Utas t21 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
x.issynca ();
}
}, "T21");
// Buat T22 baru, dan T22 akan menghubungi x.issyncb ()
Utas t22 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
y.issynca ();
}
}, "T22");
t21.start ();
t22.start (); // Mulai T22
}
public static void main (string [] args) {
Locktest2 demo = LockTest2 baru ();
demo.test2 ();
}
}
Hasil Menjalankan:
Salinan kode adalah sebagai berikut:
T21: Issynca
T22: Issynca
T21: Issynca
T22: Issynca
T21: Issynca
T22: Issynca
T21: Issynca
T22: Issynca
T21: Issynca
T22: Issynca
(03) tidak dapat diakses secara bersamaan. Karena csynca () dan csyncb () keduanya tipe statis, x.csynca () setara dengan sesuatu. ditanya pada saat yang sama.
Salinan kode adalah sebagai berikut:
// Kode Sumber Locktest3.java
kelas sesuatu {
public disinkronkan void issynca () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": issynca");
}
} catch (InterruptedException IE) {
}
}
public disinkronkan void issyncb () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": issyncb");
}
} catch (InterruptedException IE) {
}
}
public static static void csynca () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": csynca");
}
} catch (InterruptedException IE) {
}
}
public static static void csyncb () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": csyncb");
}
} catch (InterruptedException IE) {
}
}
}
Kelas publik LockTest3 {
Something x = new Something ();
Sesuatu y = new sesuatu ();
// bandingkan (03) x.csynca () dengan y.csyncb ()
private void test3 () {
// Buat T31 baru, dan T31 akan menghubungi x.issynca ()
Utas t31 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
x.csynca ();
}
}, "T31");
// Buat T32 baru, dan T32 akan menghubungi x.issyncb ()
Utas t32 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
y.csyncb ();
}
}, "T32");
t31.start ();
t32.start (); // Mulai T32
}
public static void main (string [] args) {
Demo LockTest3 = LockTest3 baru ();
demo.test3 ();
}
}
Hasil Menjalankan:
Salinan kode adalah sebagai berikut:
T31: CSYNCA
T31: CSYNCA
T31: CSYNCA
T31: CSYNCA
T31: CSYNCA
T32: csyncb
T32: csyncb
T32: csyncb
T32: csyncb
T32: csyncb
(04) dapat diakses secara bersamaan. Karena issynca () adalah metode instan, x.issynca () menggunakan kunci objek X; sementara csynca () adalah metode statis, sesuatu.csynca () dapat memahami bahwa itu adalah "kunci kelas" yang digunakan. Karena itu, mereka dapat diakses secara bersamaan.
Salinan kode adalah sebagai berikut:
// Kode Sumber Locktest4.java
kelas sesuatu {
public disinkronkan void issynca () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": issynca");
}
} catch (InterruptedException IE) {
}
}
public disinkronkan void issyncb () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": issyncb");
}
} catch (InterruptedException IE) {
}
}
public static static void csynca () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": csynca");
}
} catch (InterruptedException IE) {
}
}
public static static void csyncb () {
mencoba {
untuk (int i = 0; i <5; i ++) {
Thread.sleep (100);
System.out.println (thread.currentThread (). GetName ()+": csyncb");
}
} catch (InterruptedException IE) {
}
}
}
Kelas publik LockTest4 {
Something x = new Something ();
Sesuatu y = new sesuatu ();
// bandingkan (04) x.issynca () dengan sesuatu.csynca ()
Private void test4 () {
// Buat T41 baru, dan T41 akan menghubungi x.issynca ()
Utas t41 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
x.issynca ();
}
}, "T41");
// Buat T42 baru, dan T42 akan menghubungi x.issyncb ()
Utas t42 = utas baru (
runnable baru () {
@Mengesampingkan
public void run () {
Something.csynca ();
}
}, "T42");
t41.start ();
t42.start (); // Mulai T42
}
public static void main (string [] args) {
Locktest4 demo = LockTest4 baru ();
demo.test4 ();
}
}
Hasil Menjalankan:
Salinan kode adalah sebagai berikut:
T41: Issynca
T42: CSYNCA
T41: Issynca
T42: CSYNCA
T41: Issynca
T42: CSYNCA
T41: Issynca
T42: CSYNCA
T41: Issynca
T42: CSYNCA