八鎖現象理解鎖
接下來以程式碼為例來理解鎖的八種情況:
TimeUtils工具類:API提供的常用工具類,時間處理。
1、以下程式碼是先發簡訊還是先打電話?
package com.yy;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone. sendSms();
}).start();
try {
TimeUnit.SECONDS.sleep(1); //TimeUtil工具類提供睡眠時間
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}).start() ;
}
}
class Phone{
public synchronized void sendSms(){
System.out.println("發簡訊");
}
public synchronized void call(){
System.out.println("打電話");
}
}
這時候是先發簡訊還是先打電話?
答案是先發簡訊, 但是我們要記住,這裡不是因為hone.sendSms()先執行,而是鎖的原因。
2、以下程式碼是先發簡訊還是先打電話?
package com.yy;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
}).start();
try {
TimeUnit.SECONDS.sleep(1); //TimeUtil工具類提供睡眠時間
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}).start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發簡訊");
}
public synchronized void call(){
System.out.println("打電話");
}
}
答案依然是先發資訊再打電話。
以上兩個問題說明了不是因為執行的順序原因而是鎖的問題。
為什麼是發簡訊先執行呢?原因是Synchrinized鎖的物件是方法的呼叫者,這裡是phone,兩個方式使用的是同一把鎖,誰先拿到就會誰來使用,發簡訊的方法先拿到鎖,所以先進行發簡訊。
問題3:增加一個普通方法並呼叫,是先打印發簡訊還是列印hello?
package com.yy;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
}).start();
try {
TimeUnit.SECONDS.sleep(1); //TimeUtil工具類提供睡眠時間
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.hello();
}).start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發簡訊");
}
public synchronized void call(){
System.out.println("打電話");
}
public void hello(){
System.out.println("Hello");
}
}
先看結果:
為什麼呢?原因是因為hello方法不是同步方法,不受鎖的影響。
問題4:兩個物件,兩個同步方法,列印結果是什麼樣的?
package com.yy;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone.sendSms();
}).start();
try {
TimeUnit.SECONDS.sleep(1); //TimeUtil工具類提供睡眠時間
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
}).start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發簡訊");
}
public synchronized void call(){
System.out.println("打電話");
}
public void hello(){
System.out.println("Hello");
}
}
結果:
兩個鎖物件判斷誰先執行是根據物件呼叫的時間。
問題5:同步方法是靜態方法一下程式碼列印的結果是?
package com.yy;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
}).start();
try {
TimeUnit.SECONDS.sleep(1); //TimeUtil工具類提供睡眠時間
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}).start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發簡訊");
}
public static synchronized void call(){
System.out.println("打電話");
}
}
結果:
這裡的原因是static修飾的同步方法在類載入的時候就存在了,鎖的物件是Class,發簡訊先把呼叫。
問題6:兩個物件呼叫兩個靜態同步方法,以下程式碼的結果?
package com.yy;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone.sendSms();
}).start();
try {
TimeUnit.SECONDS.sleep(1); //TimeUtil工具類提供睡眠時間
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
}).start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發簡訊");
}
public static synchronized void call(){
System.out.println("打電話");
}
}
看結果:
為什麼呢?原因還是因為這時候的鎖物件還是Class物件,Phone和Phone2物件的Class是一樣的,所以在呼叫的時候先打印發簡訊。
問題7:一個靜態同步方法,一個普通方法,一個物件,一下程式碼的結果是?
package com.yy;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
}).start();
try {
TimeUnit.SECONDS.sleep(1); //TimeUtil工具類提供睡眠時間
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}).start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發簡訊");
}
public synchronized void call(){
System.out.println("打電話");
}
}
看結果:
解析:這時候鎖的物件是兩個,一個是Class模板,一個是Phone。屬於兩把鎖,不需要相互去等待,完全可以去根據時間來看結果。
問題8:兩個物件,一個靜態同步方法一個普通同步方法,以下程式碼的結果?
package com.yy;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone.sendSms();
}).start();
try {
TimeUnit.SECONDS.sleep(1); //TimeUtil工具類提供睡眠時間
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
}).start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發簡訊");
}
public synchronized void call(){
System.out.println("打電話");
}
}
結果:
原因還是因為程式中存在兩把鎖,鎖的物件有兩個,一個是Class模板,一個是Phone2。鎖的物件不同,相互之間不需要等待。
總結:通過以上內容我們可以知道static鎖的是this的Class模板,而普通的同步方法鎖的是方法呼叫者,如果存在兩把鎖,呼叫之間是不衝突的。