java執行緒 - synchronized詳解
synchronized : 代表同步的修飾符
目錄
1.特性:
- 互斥性(排他性) : 同一時刻,只允許一個執行緒進行訪問,會一直等待執行緒釋放鎖,容易造成死鎖。
- 重入性: 程式或者子程式可以在任意時刻被中斷然後系統排程另一段程式碼,這段程式碼又呼叫了該子程式,並不會出錯。 也 就是說,一個執行緒執行了某個程式,是可以再次進入該程式並執行。
內建鎖
2.互斥性demo:
一 . synchronized修飾普通方法:針對的是物件的某一例項。(並不影響物件的其他例項)
package com.test.concurrencyThread; public class Test implements Runnable { public synchronized void test() { try { System.out.println(Thread.currentThread().getName()); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void run() { test(); } public static void main(String[] args) { Runnable runnable = new Test(); Runnable runnable1 = new Test(); Thread thread = new Thread(runnable); thread.start(); Thread thread1 = new Thread(runnable1); thread1.start(); } }
程式輸出:
Thread-0
Thread-1
(兩者幾乎同時輸出)
package com.test.concurrencyThread; public class Test implements Runnable { public synchronized void test() { try { System.out.println(Thread.currentThread().getName()); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void run() { test(); } public static void main(String[] args) { Runnable runnable = new Test(); Thread thread = new Thread(runnable); thread.start(); Thread thread1 = new Thread(runnable); thread1.start(); } }
再看這第二個demo,由兩個執行緒啟動同一個Test的例項,輸出的結果沒變,只是在輸出thread-0之後過了5秒,才輸出Thread-1。
Thread-0
Thread-1
由此可見,synchronized修飾普通方法:針對的是物件的某一例項。(並不影響物件的其他例項)。
二. synchronized修飾靜態方法:針對的是整個類。
package com.test.concurrencyThread;
public class Test implements Runnable {
public synchronized static void test1() {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
test1();
}
public static void main(String[] args) {
Runnable runnable = new Test();
Runnable runnable1 = new Test();
Thread thread = new Thread(runnable);
thread.start();
Thread thread1 = new Thread(runnable1);
thread1.start();
}
}
接下來synchronized修飾靜態方法,無論是建立一個例項還是兩個例項,都是先輸出一個,之後過了5秒,再輸出第二個。
Thread-0
Thread-1
由此可見,synchronized修飾靜態方法:針對的是整個類。
三. synchronized修飾程式碼塊:針對的是synchronized修飾的物件,(不影響synchronized修飾的其他物件)
package com.test.concurrencyThread;
public class Test implements Runnable {
private final Object object = new Object();
private void test1() {
synchronized (object) {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
test1();
}
public static void main(String[] args) {
Runnable runnable = new Test();
Thread thread = new Thread(runnable);
thread.start();
Thread thread1 = new Thread(runnable);
thread1.start();
}
}
建立一個Test例項,由兩個執行緒執行。結果也是先輸出一個,過五秒之後,又輸出第二個。
Thread-0
Thread-1
package com.test.concurrencyThread;
public class Test implements Runnable {
private final Object object = new Object();
private void test1() {
synchronized (object) {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
test1();
}
public static void main(String[] args) {
Runnable runnable = new Test();
Runnable runnable1 = new Test();
Thread thread = new Thread(runnable);
thread.start();
Thread thread1 = new Thread(runnable1);
thread1.start();
}
}
Thread-0
Thread-1
結果幾乎同時輸出。由此可見,synchronized修飾程式碼塊:針對的是synchronized修飾的物件,(不影響其他synchronized修飾的物件)。
3.重入性講解
當一個執行緒請求一個由其他執行緒持有的物件鎖時,該執行緒會阻塞。當執行緒請求自己持有的物件鎖時,如果該執行緒是重入鎖,請求就會成功,否則阻塞。
我們回來看synchronized,synchronized擁有強制原子性的內部鎖機制,是一個可重入鎖。因此,在一個執行緒使用synchronized方法時呼叫該物件另一個synchronized方法,即一個執行緒得到一個物件鎖後再次請求該物件鎖,是永遠可以拿到鎖的。
在Java內部,同一個執行緒呼叫自己類中其他synchronized方法/塊時不會阻礙該執行緒的執行,同一個執行緒對同一個物件鎖是可重入的,同一個執行緒可以獲取同一把鎖多次,也就是可以多次重入。原因是Java中執行緒獲得物件鎖的操作是以執行緒為單位的,而不是以呼叫為單位的。