Android 技能圖譜學習路線系列-Java基礎之方法鎖、物件鎖、類鎖
先了解一下Synchronized的用法。 一、Synchronized的用法 在修飾程式碼塊的時候需要一個reference物件作為鎖的物件。 在修飾方法的時候預設是當前物件作為鎖的物件。 在修飾類時候預設是當前類的Class物件作為鎖的物件。
二、三種鎖得區別與用法 1、方法鎖(synchronized修飾方法時) 在定義方法時加入 synchronized關鍵字來宣告 synchronized 方法。 synchronized 方法控制對類成員變數的訪問: 每個類例項對應一把鎖,每個 synchronized 方法都必須獲得呼叫該方法的類例項的鎖方能執行,否則所屬執行緒阻塞,方法一旦執行,就獨佔該鎖,直到從該方法返回時才將鎖釋放,此後被阻塞的執行緒方能獲得該鎖,重新進入可執行狀態。這種機制確保了同一時刻對於每一個類例項,其所有宣告為 synchronized 的成員函式中至多隻有一個處於可執行狀態,從而有效避免了類成員變數的訪問衝突。 看一下程式碼:
public class TestSynchronizedMethod { private int count = 0; public static void main(String[] args) { TestSynchronizedMethod testSynchronized = new TestSynchronizedMethod(); Thread threadA = new Thread(new Runnable() { @Override public void run() { System.out.println("threadA開始執行"); testSynchronized.methodASynchronized(); System.out.println("threadA結束"); } }); threadA.start(); Thread threadB = new Thread(new Runnable() { @Override public void run() { System.out.println("threadB開始執行"); testSynchronized.methodBSynchronized(); System.out.println("threadB結束"); } }); threadB.start(); } public synchronized void methodASynchronized(){ System.out.println("methodASynchronized獲得鎖"); for(int i=0;i<5;i++){ count++; System.out.println("methodASynchronized-count="+count); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("methodASynchronized釋放鎖"); } public synchronized void methodBSynchronized(){ System.out.println("methodBSynchronized獲得鎖"); for(int i=0;i<5;i++){ count++; System.out.println("methodBSynchronized-count="+count); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("methodBSynchronized釋放鎖"); } }
輸出結果:
threadA開始執行 methodASynchronized獲得鎖 methodASynchronized-count=1 threadB開始執行 methodASynchronized-count=2 methodASynchronized-count=3 methodASynchronized-count=4 methodASynchronized-count=5 methodASynchronized釋放鎖 threadA結束 methodBSynchronized獲得鎖 methodBSynchronized-count=6 methodBSynchronized-count=7 methodBSynchronized-count=8 methodBSynchronized-count=9 methodBSynchronized-count=10 methodBSynchronized釋放鎖 threadB結束
從輸出結果可以看出,加了同步鎖的兩個方法,在不同的執行緒中呼叫後,先執行的方法會先獲得鎖,全部執行完畢後才釋放鎖,之後其他方法繼續執行。
2、物件鎖(synchronized修飾方法或程式碼塊) 當一個物件中有synchronized method或synchronized block的時候呼叫此物件的同步方法或進入其同步區域時,就必須先獲得物件鎖。如果此物件的物件鎖已被其他呼叫者佔用,則需要等待此鎖被釋放。(方法鎖也是物件鎖中的一種)
執行緒進入synchronized方法或synchronized 塊的時候獲取該物件的鎖(這個鎖由JVM自動獲取和釋放),如果已經有執行緒獲取了這個物件的鎖,則當前執行緒會等待;synchronized方法或synchronized 塊正常返回或者拋異常而終止,JVM會自動釋放物件鎖。 物件鎖的兩種方式: (1)方法鎖也是物件鎖
public synchronized void methodASynchronized(){
System.out.println("methodASynchronized 我是方法鎖也是物件鎖");
}
(2)物件鎖,程式碼塊形式
public void methodBSynchronized(){
synchronized (this) {
System.out.println("methodBSynchronized 我是物件鎖");
}
}
看一下程式碼:
public class TestSynchronizedObject {
private int count = 0;
public static void main(String[] args) {
TestSynchronizedObject testSynchronized = new TestSynchronizedObject();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadA開始執行");
testSynchronized.methodASynchronized();
System.out.println("threadA結束");
}
});
threadA.start();
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadB開始執行");
testSynchronized.methodBSynchronizedBlock();
System.out.println("threadB結束");
}
});
threadB.start();
}
public synchronized void methodASynchronized(){
System.out.println("methodASynchronized獲得鎖");
System.out.println("methodASynchronized 我是方法鎖也是物件鎖");
for(int i=0;i<5;i++){
count++;
System.out.println("methodASynchronized-count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("methodASynchronized釋放鎖");
}
public void methodBSynchronizedBlock(){
synchronized (this) {
System.out.println("methodBSynchronizedBlock獲得鎖");
System.out.println("methodBSynchronizedBlock 我是物件鎖");
for(int i=0;i<5;i++){
count++;
System.out.println("methodBSynchronizedBlock-count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("methodBSynchronizedBlock釋放鎖");
}
}
}
執行結果如下:
threadA開始執行
methodASynchronized獲得鎖
methodASynchronized 我是方法鎖也是物件鎖
methodASynchronized-count=1
threadB開始執行
methodASynchronized-count=2
methodASynchronized-count=3
methodASynchronized-count=4
methodASynchronized-count=5
methodASynchronized釋放鎖
threadA結束
methodBSynchronizedBlock獲得鎖
methodBSynchronizedBlock 我是物件鎖
methodBSynchronizedBlock-count=6
methodBSynchronizedBlock-count=7
methodBSynchronizedBlock-count=8
methodBSynchronizedBlock-count=9
methodBSynchronizedBlock-count=10
methodBSynchronizedBlock釋放鎖
threadB結束
從輸出結果可以看出,在同一個物件中,不論是同步方法還是同步塊,在不同的執行緒中呼叫後,先執行的方法會先獲得鎖,全部執行完畢後才釋放鎖,之後其他方法繼續執行。
3、類鎖(synchronized 修飾靜態的方法或程式碼塊) 一個class不論被例項化多少次,它的靜態方法或靜態變數在記憶體中都只有一份。所以,一旦一個靜態的方法被申明為synchronized。此類所有的例項化物件在呼叫此方法時共用同一把鎖,稱之為類鎖。
與物件鎖不同的是,物件鎖是用來控制例項方法之間的同步,類鎖是用來控制靜態方法(或靜態變數互斥體)之間的同步。
類鎖的兩種方式: (1)靜態方法
public static synchronized void methodASynchronizedClass(){
System.out.println("methodASynchronizedClass我是靜態方法");
}
(2)程式碼塊
public void methodBSynchronizedClass(){
synchronized (TestSynchronizedClass.class) {
System.out.println("methodASynchronizedClass我是同步程式碼塊");
}
}
看一下程式碼:
public class TestSynchronizedClass {
private static int count = 0;
public static void main(String[] args) {
TestSynchronizedClass testSynchronized1 = new TestSynchronizedClass();
TestSynchronizedClass testSynchronized2 = new TestSynchronizedClass();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadA開始執行");
testSynchronized1.methodASynchronizedClass();
System.out.println("threadA結束");
}
});
threadA.start();
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadB開始執行");
testSynchronized2.methodBSynchronizedClass();
System.out.println("threadB結束");
}
});
threadB.start();
}
public static synchronized void methodASynchronizedClass(){
System.out.println("methodASynchronizedClass獲得鎖");
System.out.println("methodASynchronizedClass我是靜態方法");
for(int i=0;i<5;i++){
count++;
System.out.println("methodASynchronizedClass-count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("methodASynchronizedClass釋放鎖");
}
public void methodBSynchronizedClass(){
synchronized (TestSynchronizedClass.class) {
System.out.println("methodBSynchronizedClass獲得鎖");
System.out.println("methodASynchronizedClass我是同步程式碼塊");
for(int i=0;i<5;i++){
count++;
System.out.println("methodBSynchronizedClass-count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("methodBSynchronizedClass釋放鎖");
}
}
}
執行結果如下:
threadA開始執行
methodASynchronizedClass獲得鎖
methodASynchronizedClass我是靜態方法
methodASynchronizedClass-count=1
threadB開始執行
methodASynchronizedClass-count=2
methodASynchronizedClass-count=3
methodASynchronizedClass-count=4
methodASynchronizedClass-count=5
methodASynchronizedClass釋放鎖
threadA結束
methodBSynchronizedClass獲得鎖
methodASynchronizedClass我是同步程式碼塊
methodBSynchronizedClass-count=6
methodBSynchronizedClass-count=7
methodBSynchronizedClass-count=8
methodBSynchronizedClass-count=9
methodBSynchronizedClass-count=10
methodBSynchronizedClass釋放鎖
threadB結束
從輸出結果可以看出,在同一個class物件的不同例項中,不論是同步方法還是同步塊,在不同的執行緒中呼叫後,先執行的方法會先獲得類鎖,全部執行完畢後才釋放鎖,之後其他方法繼續執行。