Lock(本地同步)鎖的8種情況

總結與說明:

 * 題目:
* 1.標準訪問,請問是先列印郵件還是簡訊 Email
* 2.email方法新增暫停4秒鐘,請問是先列印郵件還是簡訊 Email
* 3.新增普通的hello方法,請問先列印郵件還是hello hello
* 4.兩部手機,請問先列印郵件還是簡訊 SMS
* 5.兩個靜態同步方法,1部手機,請問先列印郵件還是簡訊 Email
* 6.兩個靜態同步方法,2部手機,請問先列印郵件還是簡訊 Email
* 7.一個普通同步方法,一個靜態同步方法,1部手機,請問先列印郵件還是簡訊 SMS
* 8.一個普通同步方法,一個靜態同步方法,2部手機,請問先列印郵件還是簡訊 SMS
*
* lock1、2
* 一個物件裡面如果有多個synchronized方法,某一時刻內,只要一個執行緒去呼叫其中的一個synchronized方法,
* 其他的執行緒都只能等待,換句話說,某一時刻內,只能有唯一一個執行緒去訪問這些synchronized方法,
* 鎖的是當前物件的this,被鎖定後,其它的執行緒都不能進入到當前物件的其它的synchronized方法
*
* lock3、4
* 加個普通的方法後和同步鎖無關
* 換成兩個物件後,不是同一把鎖了,情況立刻變化
*
* lock5、6
* 都換成靜態同步方法後,情況又變化(靜態鎖的是Class類物件)
* 若是普通同步方法,new this,具體的一部一部手機,所有的普通同步方法用的都是同一把鎖----示例物件本身
* 若是靜態同步方法,static class,唯一的一個模板
* synchronized是實現同步的基礎:Java中的每一個物件都可以作為鎖
* 具體表現為一下3種形式。
* 對於普通同步方法,鎖是當前例項物件。它等同於 對於同步方法塊,鎖是synchronized括號裡的配置的物件。
* 對於靜態同步方法,鎖是當前類的Class類元資訊
*
* lock7、8
* 當一個執行緒試圖訪問同步程式碼塊時它首先必須得到鎖,退出或丟擲異常時必須釋放鎖
*
* 所有的同步方法用的都是同一把鎖----例項物件本身,就是new出來的具體例項物件本身
* 也就是說如果一個例項物件的普通同步方法獲取鎖後,該例項物件的其他普通同步方法必須等待獲取鎖的方法釋放鎖後才能獲得鎖
* 可是別的例項物件的普通同步方法因為跟該例項物件的普通同步方法用到是不同鎖,所以不用等待該例項物件已獲取鎖的普通
* 同步方法釋放鎖就可以獲取它們自己的鎖
*
* 所有的靜態同步方法用的也是同一把鎖----類物件本身,就是我們說過的唯一模板Class
* 具體例項物件this和唯一模板Class,這兩把鎖是兩個不同的物件,所有靜態同步方法與普通同步方法之間是不會有競態條件的,
* 但是一旦一個靜態同方法獲取鎖後,其他的靜態同步方法都必須等待該方法釋放鎖後才能獲取鎖。

lock1、2、3一個例項物件

lock4、5、6、7、8兩個例項物件

lock1

1.標準訪問,請問是先列印郵件還是簡訊? 答案:Email

sendEmail()與sendSms()都是普通同步方法,都使用synchronized鎖

這裡按方法呼叫順序,依次執行,當前synchronized鎖住的是同一個例項物件

package day02_lam;

import java.util.concurrent.TimeUnit;

class Phone{
public synchronized void sendEmail() throws InterruptedException{
System.out.println("------------sendEmail");
} public synchronized void sendSms(){
System.out.println("------------sendSMS");
} } /**
*題目:
*1.標準訪問,請問是先列印郵件還是簡訊 Email
*/
public class Lock8{
public static void main(String[] args) throws InterruptedException{
Phone phone=new Phone();
//執行緒A傳送Email
new Thread(()->{
try{
phone.sendEmail();
}catch(InterruptedExceptione){
e.printStackTrace();
}
},"A").start(); Thread.sleep(300); //執行緒B傳送簡訊
newThread(()->{
phone.sendSms();
},"B").start();
}
}

lock2

2.email方法新增暫停4秒鐘,請問是先列印郵件還是簡訊? 答案:Email

lock2是lock1的變形,其原理與lock1一致,synchronized鎖住的是同一個物件,必須等到第一個方法把鎖釋放後,第二個方法才能夠獲得鎖

package day02_lam;

import java.util.concurrent.TimeUnit;

class Phone{
public synchronized void sendEmail()throwsInterruptedException{
//睡眠4秒種
TimeUnit.SECONDS.sleep(4);
System.out.println("------------sendEmail");
} public synchronized void sendSms(){
System.out.println("------------sendSMS");
} } /**
*題目:
*1.標準訪問,請問是先列印郵件還是簡訊Email
*2.email方法新增暫停4秒鐘,請問是先列印郵件還是簡訊Email
*/
public class Lock8{
public static void main(String[] args)throws InterruptedException{
//資源類
Phone phone=new Phone(); //執行緒A sendEmail
newThread(()->{
try{
phone.sendEmail();
}catch(InterruptedExceptione){
e.printStackTrace();
}
},"A").start(); //這裡保證執行緒A能夠執行完
Thread.sleep(300); //執行緒B sendSms
newThread(()->{
phone.sendSms();
},"B").start(); }
}

結果:等待4秒中後出現sendEmail與sendSMS同時輸出

lock3

3.新增普通的hello方法,請問先列印郵件還是hello? 答案:hello

只有新增synchronized關鍵字的方法才會被synchronized鎖控制

package day02_lam;

import java.util.concurrent.TimeUnit;

class Phone{
public synchronized void sendEmail()throws InterruptedException{
TimeUnit.SECONDS.sleep(4);
System.out.println("------------sendEmail");
} public synchronized void sendSms(){
System.out.println("------------sendSMS");
} public void hello(){//未加synchronized
System.out.println("hello");
}
} /**
*題目:
*1.標準訪問,請問是先列印郵件還是簡訊Email
*2.email方法新增暫停4秒鐘,請問是先列印郵件還是簡訊Email
*3.新增普通的hello方法,請問先列印郵件還是hello hello
*/
public class Lock8{
public static void main(String[] args)throws InterruptedException{
Phone phone=new Phone(); //執行緒A呼叫sendEmail
new Thread(()->{
try{
phone.sendEmail();
}catch(InterruptedExceptione){
e.printStackTrace();
}
},"A").start(); Thread.sleep(300); //執行緒B呼叫普通方法hello
new Thread(()->{
//phone.sendSms();
phone.hello();
},"B").start(); }
}

結果:hello先輸出,4s後sendEmail再輸出

lock4

4.兩部手機,請問先列印郵件還是簡訊 ? 答案:SMS

不同例項物件,synchronized鎖住的是對應的呼叫物件

package day02_lam;

import java.util.concurrent.TimeUnit;

class Phone{
public synchronized void sendEmail()throws InterruptedException{
//睡眠4秒種
TimeUnit.SECONDS.sleep(4);
System.out.println("------------sendEmail");
} public synchronized void sendSms(){
System.out.println("------------sendSMS");
} public void hello(){
System.out.println("hello");
}
} /**
*題目:
*1.標準訪問,請問是先列印郵件還是簡訊Email
*2.email方法新增暫停4秒鐘,請問是先列印郵件還是簡訊Email
*3.新增普通的hello方法,請問先列印郵件還是hellohello
*4.兩部手機,請問先列印郵件還是簡訊 SMS
*/
public class Lock8{
public static void main(String[] args)throws InterruptedException{
Phone phone=new Phone();
Phone phone2=new Phone(); //sychronized鎖定是當前物件對應的this,phone與phone2分別鎖的是自己的this //執行緒A使用例項phone呼叫sendEmail
newThread(()->{
try{
//sendEmail方法中包含睡眠4秒種
phone.sendEmail();
}catch(InterruptedExceptione){
e.printStackTrace();
}
},"A").start(); Thread.sleep(300); //執行緒B使用例項phone2呼叫sendSms
newThread(()->{
phone2.sendSms();
//phone.hello();
},"B").start(); }
}

結果:sendSMS先輸出,4s後sendEmail再輸出

lock5

5.兩個靜態同步方法,1部手機,請問先列印郵件還是簡訊? 答案:Email

synchronized鎖靜態方法,實際是鎖住的類元資訊,因為靜態方法是隨類元資訊的載入而儲存到jvm的靜態區,是所有例項建立的模板

package day02_lam;

import java.util.concurrent.TimeUnit;

class Phone{
public static synchronized void sendEmail()throws InterruptedException{
TimeUnit.SECONDS.sleep(4);
System.out.println("------------sendEmail");
} public static synchronized void sendSms(){ System.out.println("------------sendSMS");
} public void hello(){
System.out.println("hello");
}
} /**
*題目:
*1.標準訪問,請問是先列印郵件還是簡訊Email
*2.email方法新增暫停4秒鐘,請問是先列印郵件還是簡訊Email
*3.新增普通的hello方法,請問先列印郵件還是hellohello
*4.兩部手機,請問先列印郵件還是簡訊SMS
*5.兩個靜態同步方法,1部手機,請問先列印郵件還是簡訊 Email
*/
public class Lock8{
public static void main(String[] args)throws InterruptedException{
Phone phone=new Phone(); new Thread(()->{
try{
phone.sendEmail();
}catch(InterruptedException e){
e.printStackTrace();
}
},"A").start(); Thread.sleep(300); newThread(()->{
phone.sendSms();
},"B").start(); }
}

結果:4秒種後,sendEmail率先輸出,sendSMS緊隨其後

lock6

6.兩個靜態同步方法,2部手機,請問先列印郵件還是簡訊? 答案:Email

與lock5的執行結果一致,當前synchronized鎖住的是類元資訊

package day02_lam;

import java.util.concurrent.TimeUnit;

class Phone{
public static synchronized void sendEmail() throws InterruptedException{
TimeUnit.SECONDS.sleep(4);
System.out.println("------------sendEmail");
} public static synchronized void sendSms(){
System.out.println("------------sendSMS");
} public void hello(){
System.out.println("hello");
}
} /**
*題目:
*1.標準訪問,請問是先列印郵件還是簡訊Email
*2.email方法新增暫停4秒鐘,請問是先列印郵件還是簡訊Email
*3.新增普通的hello方法,請問先列印郵件還是hellohello
*4.兩部手機,請問先列印郵件還是簡訊SMS
*5.兩個靜態同步方法,1部手機,請問先列印郵件還是簡訊Email
*6.兩個靜態同步方法,2部手機,請問先列印郵件還是簡訊Email
*/
public class Lock8{
publicstaticvoidmain(String[]args)throwsInterruptedException{
Phone phone=new Phone();
Phone phone2=new Phone(); new Thread(()->{
try{
phone.sendEmail();
}catch(InterruptedException e){
e.printStackTrace();
}
},"A").start();
Thread.sleep(300);
new Thread(()->{
//phone.sendSms();
phone2.sendSms();
//phone.hello();
},"B").start(); }
}

結果:與lock5的執行結果一致,sendEmail與sendSMS都是4s後進行輸出,synchronized也是鎖住的類元資訊

lock7

7.一個普通同步方法,一個靜態同步方法,1部手機,請問先列印Email還是SMS? 答案:SMS

原理:普通同步方法,synchronized鎖住的是當前例項物件,當前例項物件存在於jvm的堆記憶體區

靜態同步方法,synchronized鎖住的是當前類的類元資訊,存在於jvm元空間的靜態區中

package day02_lam;

import java.util.concurrent.TimeUnit;

class Phone{
//靜態同步方法
public static synchronized void sendEmail()throws InterruptedException{
TimeUnit.SECONDS.sleep(4);
System.out.println("------------sendEmail");
} //普通同步方法
public synchronized void sendSms(){//無static System.out.println("------------sendSMS");
} public void hello(){
System.out.println("hello");
}
} /**
*題目:
*1.標準訪問,請問是先列印郵件還是簡訊Email
*2.email方法新增暫停4秒鐘,請問是先列印郵件還是簡訊Email
*3.新增普通的hello方法,請問先列印郵件還是hellohello
*4.兩部手機,請問先列印郵件還是簡訊SMS
*5.兩個靜態同步方法,1部手機,請問先列印郵件還是簡訊Email
*6.兩個靜態同步方法,2部手機,請問先列印郵件還是簡訊Email
*7.一個普通同步方法,一個靜態同步方法,1部手機,請問先列印郵件還是簡訊 SMS
*/
public class Lock8{
public static void main(String[] args) throws InterruptedException{
Phonephone=newPhone();
Phonephone2=newPhone(); newThread(()->{
try{
phone.sendEmail();
}catch(InterruptedExceptione){
e.printStackTrace();
}
},"A").start(); Thread.sleep(300); newThread(()->{
phone.sendSms();
//phone2.sendSms();
//phone.hello();
},"B").start(); }
}

結果:sendSMS先輸出,4s後sendEmail再輸出

lock8

8.一個普通同步方法,一個靜態同步方法,2部手機,請問先列印郵件還是簡訊? 答案:SMS

原理:與lock7一致

package day02_lam;

import java.util.concurrent.TimeUnit;

class Phone{
public static synchronized void sendEmail()throwsInterruptedException{
TimeUnit.SECONDS.sleep(3);
System.out.println("------------sendEmail");
} public synchronized void sendSms(){ System.out.println("------------sendSMS");
} public void hello(){
System.out.println("hello");
}
} /**
*題目:
*1.標準訪問,請問是先列印郵件還是簡訊Email
*2.email方法新增暫停4秒鐘,請問是先列印郵件還是簡訊Email
*3.新增普通的hello方法,請問先列印郵件還是hellohello
*4.兩部手機,請問先列印郵件還是簡訊SMS
*5.兩個靜態同步方法,1部手機,請問先列印郵件還是簡訊Email
*6.兩個靜態同步方法,2部手機,請問先列印郵件還是簡訊Email
*7.一個普通同步方法,一個靜態同步方法,1部手機,請問先列印郵件還是簡訊SMS
*8.一個普通同步方法,一個靜態同步方法,2部手機,請問先列印郵件還是簡訊SMS
*/
public class Lock8{
publicstaticvoidmain(String[]args)throwsInterruptedException{
Phone phone=new Phone();
Phone phone2=new Phone(); new Thread(()->{
try{
phone.sendEmail();
}catch(InterruptedExceptione){
e.printStackTrace();
}
},"A").start(); Thread.sleep(300); new Thread(()->{
//phone.sendSms();
phone2.sendSms();
//phone.hello();
},"B").start(); }
}

結果:執行結果與lock7一致,sendSMS先輸出,4s後sendEmail再輸出