引入
今天我們將通過幾個場景來深刻的理解幾個問題,
如下:
- 什么是鎖?
- 是誰的鎖?
- 鎖的是誰?
場景1
import java.util.concurrent.TimeUnit;
/**
* @ClassName test1
* @Description
* @Author SkySong
* @Date 2020-10-11 18:13
*/
public class test1 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
phone.sendMsm();
},"A").start();
//等待1秒鐘
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{ //資源類
public synchronized void sendMsm(){
System.out.println("發短信");
}
public synchronized void call(){
System.out.println("打電話");
}
}
簡單說明一下:一個資源類里面有兩個同步方法(發短信 和 打電話),然后主執行緒創建了一個資源類物件,并開了兩個執行緒去分別執行這個物件的兩個同步方法,
執行結果:
發短信
打電話
分析:
執行緒A先拿到了 phone 這個物件的鎖,所以他先執行,
這里 不 要認為是主執行緒先呼叫了A執行緒所以他先執行,
不理解的話,我們再看一個場景
場景2
import java.util.concurrent.TimeUnit;
/**
* @ClassName test1
* @Description
* @Author SkySong
* @Date 2020-10-11 18:13
*/
@SuppressWarnings("ALL")
public class test1 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendMsm();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
//等待1秒鐘
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
public synchronized void sendMsm() throws InterruptedException {
//等待3秒鐘
TimeUnit.SECONDS.sleep(3);
System.out.println("發短信");
}
public synchronized void call(){
System.out.println("打電話");
}
}
我們讓 “發短信”方法 持續 3 秒鐘,
執行結果:
發短信
打電話
分析:
結果還是先 發短信 后 打電話,就像我 剛剛說的一樣,發短信是持續3秒,并不影響他先得到鎖,因為主執行緒過了一秒才去呼叫執行緒B,所以執行緒A 99.9%的幾率是先拿到鎖的,
synchronized 鎖的物件是方法的呼叫者,
執行緒A和B 都是呼叫的同一個物件(phone)的方法,所以他們都是去競爭同一把鎖,誰先拿到鎖誰先執行,沒拿到就得等,
看下一個場景:
如果沒有鎖競爭關系,那么應該就是拼的速度了,
場景3
import java.util.concurrent.TimeUnit;
/**
* @ClassName test1
* @Description
* @Author SkySong
* @Date 2020-10-11 18:13
*/
@SuppressWarnings("ALL")
public class test1 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendMsm();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
//等待1秒鐘
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.hello();
},"B").start();
}
}
class Phone{
public synchronized void sendMsm() throws InterruptedException {
//等待3秒鐘
TimeUnit.SECONDS.sleep(3);
System.out.println("發短信");
}
public synchronized void call(){
System.out.println("打電話");
}
public void hello(){
System.out.println("Hello World!");
}
}
這次我們讓執行緒B呼叫 物件phone的 hello()方法,注意:hello()方法不是同步方法!!
執行結果:
Hello World!
發短信
分析:
因為 hello()方法不是同步方法,所以壓根就沒有鎖競爭這一說,雖然 執行緒B 比 執行緒A 晚啟動了一秒鐘,但 “發短信” 要等三秒鐘才列印,這樣一來,Hello World!還會比 “發短信” 先列印 2 秒鐘,
同樣沒有鎖競爭的場景
如下:
場景4
import java.util.concurrent.TimeUnit;
/**
* @ClassName test1
* @Description
* @Author SkySong
* @Date 2020-10-11 18:13
*/
@SuppressWarnings("ALL")
public class test1 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendMsm();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//等待1秒鐘
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone1.call();
}, "B").start();
}
}
class Phone {
public synchronized void sendMsm() throws InterruptedException {
//等待3秒鐘
TimeUnit.SECONDS.sleep(3);
System.out.println("發短信");
}
public synchronized void call() {
System.out.println("打電話");
}
}
這次我們讓 執行緒A 呼叫物件phone的“發短信”方法,而讓 執行緒B 呼叫物件phone1的“打電話”方法,
執行結果:
打電話
發短信
分析:
這場景和上一個場景類似,雖然 “打電話”方法也是同步方法,但它是屬于 物件 phone1 的方法,和物件 phone 的 “發短信” 方法 沒有 鎖競爭 關系,
還是那句話:
synchronized 鎖的物件是方法的呼叫者,
顯然,這次的這兩個方法不是同一個呼叫者,而是兩個,物件phone 和 物件phone1,
物件可以鎖,那么類可不可以鎖?
可以的,看如下場景:
場景5
import java.util.concurrent.TimeUnit;
/**
* @ClassName test1
* @Description
* @Author SkySong
* @Date 2020-10-11 18:13
*/
@SuppressWarnings("ALL")
public class test1 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendMsm();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//等待1秒鐘
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone1.call();
}, "B").start();
}
}
class Phone {
public static synchronized void sendMsm() throws InterruptedException {
//等待3秒鐘
TimeUnit.SECONDS.sleep(3);
System.out.println("發短信");
}
public static synchronized void call() {
System.out.println("打電話");
}
}
基本沒什么變化,就是把 Phone 資源類 里的兩個方法都加上了 static ,讓其變成了 靜態方法,
執行結果:
發短信
打電話
分析:
被 static 關鍵字修飾的,會在類加載的時候被執行,他們會和類資訊一樣,被存到 方法區,是獨一無二的存在,
所以不管方法的呼叫者是不是同一個物件,他們都會存在 鎖競爭關系,因為他們都是同一個類的靜態方法,所以,這里的鎖,鎖的是 Class,
判斷 鎖競爭關系的關鍵,就是去看兩個同步方法是不是去拿同一把鎖,
場景6
import java.util.concurrent.TimeUnit;
/**
* @ClassName test1
* @Description
* @Author SkySong
* @Date 2020-10-11 18:13
*/
@SuppressWarnings("ALL")
public class test1 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendMsm();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//等待1秒鐘
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone.hello();
}, "B").start();
}
}
class Phone {
public static synchronized void sendMsm() throws InterruptedException {
//等待3秒鐘
TimeUnit.SECONDS.sleep(3);
System.out.println("發短信");
}
public static synchronized void call() {
System.out.println("打電話");
}
public static void hello(){
System.out.println("Hello World!");
}
}
這次我們又加上了 hello() 方法,我們把它定義成了靜態方法,同樣不是同步方法,
執行結果:
Hello World!
發短信
分析:
非同步方法,根本沒有鎖競爭這一說,同一個類(Class)也沒用,
這個場景其實挺沒勁的,都老生常談的事兒了,
那我們在來看下一個場景:
場景7
import java.util.concurrent.TimeUnit;
/**
* @ClassName test1
* @Description
* @Author SkySong
* @Date 2020-10-11 18:13
*/
@SuppressWarnings("ALL")
public class test1 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendMsm();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//等待1秒鐘
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone {
public static synchronized void sendMsm() throws InterruptedException {
//等待3秒鐘
TimeUnit.SECONDS.sleep(3);
System.out.println("發短信");
}
public synchronized void call() {
System.out.println("打電話");
}
}
這次我們把 執行緒B 呼叫的 call()方法 定義成了 普通同步方法,由同一個物件phone 去呼叫這兩個方法,
大家猜猜結果會是神馬?
同一個 物件phone 呼叫的,都是同步方法,結果難道是…
好了,不誤導大家了,
執行結果:
打電話
發短信
分析:
還是那句話,看看他們是不是競爭同一把鎖,
首先,“發短信” 是靜態方法,他應該是要去找 Class鎖,而 “打電話” 是非靜態方法,所以他找的是物件鎖,顯然,他們競爭的不是同一把鎖,所以沒有競爭關系,
換句話說,靜態同步塊鎖的是Class,而非靜態的同步塊 鎖的是呼叫者,
That’s all,,Thank you !
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/171366.html
標籤:其他
上一篇:Java 類與物件
