Java多线程之8Lock问题解析
生活随笔
收集整理的這篇文章主要介紹了
Java多线程之8Lock问题解析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Java多線程之8Lock問題解析
本文目錄
1. 8Lock實例:
2. 8Lock總結
3. 補充:當前類的Class對象和當前類的實例對象分別是什么?
8Lock實例:
1. 標準訪問的時候,請問先打印郵件還是短信?
代碼:
//資源類 class Phone{public synchronized void sendEmail() {System.out.println("sendEmail----------");}public synchronized void getSMS() {System.out.println("----------getSMS");} } //主方法 public class Lock_8 {public static void main(String[] args) {Phone p=new Phone();new Thread(() ->{p.sendEmail();},"A").start();new Thread(() ->{p.getSMS();;},"B").start();} }結果:
解析:
- 不知道,因為線程誰搶到了誰執(zhí)行,一般情況下是A執(zhí)行
2. sendEmail方法暫停4秒鐘,請問先打印郵件還是短信?
代碼:
//資源類 class Phone{public synchronized void sendEmail() {Thread.sleep(4000);System.out.println("sendEmail----------");}public synchronized void getSMS() {System.out.println("----------getSMS");} } //主方法 public class Lock_8 {public static void main(String[] args) throws Exception {Phone p=new Phone();new Thread(() ->{p.sendEmail();},"A").start();//強制讓線程A先執(zhí)行Thread.sleep(200);new Thread(() ->{p.getSMS();;},"B").start();} }結果:
解析:
- 4秒后A先執(zhí)行,緊接著B執(zhí)行。
- 因為休眠main線程之前A已經啟動了,時間足夠A運行,A會鎖住資源類的入口(也就是對象)。所以A先執(zhí)行。
3. 新增Hello普通方法,請問先打印郵件還是Hello?
代碼:
//資源類 class Phone{public synchronized void sendEmail() throws Exception {TimeUnit.SECONDS.sleep(4);System.out.println("sendEmail----------");}public synchronized void getSMS() {System.out.println("----------getSMS");}public void sayHello() {System.out.println("---hello---");} } //主方法 public class Lock_8 {public static void main(String[] args) throws Exception {Phone p=new Phone();new Thread(() ->{try {p.sendEmail();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}},"A").start();//強制讓線程A先執(zhí)行Thread.sleep(200);new Thread(() ->{p.sayHello();},"B").start();} }結果:
解析:
- 先打印hello,雖然sendEmail鎖住了資源類的入口(對象),因為sayHello未上鎖,所以sayHello方法可以進入資源類。
- 但是Thread.sleep()鎖住了main線程,也就是hello打印出來的延遲時間就是休眠的設定時間。
加個普通方法后發(fā)現和同步鎖無關
4. 兩部手機,請問先打印郵件還是短信?
代碼:
//資源類 class Phone{public synchronized void sendEmail() throws Exception {TimeUnit.SECONDS.sleep(4);System.out.println("sendEmail----------");}public synchronized void getSMS() {System.out.println("----------getSMS");}public void sayHello() {System.out.println("---hello---");} } //主方法 public class Lock_8 {public static void main(String[] args) throws Exception {Phone p1=new Phone();Phone p2=new Phone();new Thread(() ->{try {p1.sendEmail();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}},"A").start();//強制讓線程A先執(zhí)行Thread.sleep(2000);new Thread(() ->{p2.getSMS();},"B").start();} }結果:
解析:
- 先打印SMS,因為鎖的是資源類的入口,也就是對象,既然不是同一個對象,那肯定鎖不住。并且線程B的執(zhí)行和main線程的休眠時間相關。
換成兩個對象后,不是同一把鎖了,情況立刻變化。
5. 兩個靜態(tài)同步方法,同1部手機 ,請問先打印郵件還是短信?
代碼:
//資源類 class Phone{public static synchronized void sendEmail() throws Exception {TimeUnit.SECONDS.sleep(4);System.out.println("sendEmail----------");}public static synchronized void getSMS() {System.out.println("----------getSMS");}public void sayHello() {System.out.println("---hello---");} } //主方法 public class Lock_8 {public static void main(String[] args) throws Exception {Phone p=new Phone();new Thread(() ->{try {p.sendEmail();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}},"A").start();//強制讓線程A先執(zhí)行Thread.sleep(5000);new Thread(() ->{p.getSMS();},"B").start();} }結果:
解析:
- 先打印sendEmali,然后打印getSMS,因為靜態(tài)同步鎖,鎖的是Phone.Class,所以B線程在A線程執(zhí)行完之前進不去。
- synchronized實現同步的基礎:Java中的每一個對象都可以作為鎖。
具體表現為以下3種形式。
1. 對于普通同步方法,鎖是當前實例對象。
2. 對于靜態(tài)同步方法,鎖是當前類的Class對象。
3. 對于同步方法塊,鎖是Synchonized括號里配置的對象。
6. 兩個靜態(tài)同步方法,有2部手機 ,請問先打印郵件還是短信?
代碼:
//資源類 class Phone{public static synchronized void sendEmail() throws Exception {TimeUnit.SECONDS.sleep(4);System.out.println("sendEmail----------");}public static synchronized void getSMS() {System.out.println("----------getSMS");}public void sayHello() {System.out.println("---hello---");} } //主方法 public class Lock_8 {public static void main(String[] args) throws Exception {Phone p1=new Phone();Phone p2=new Phone();new Thread(() ->{try {p1.sendEmail();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}},"A").start();//強制讓線程A先執(zhí)行Thread.sleep(5000);new Thread(() ->{p2.getSMS();},"B").start();} }結果:
解析:
- 看似和5不一樣,其實是一回事,雖然兩個對象,因為強制A先執(zhí)行,所以A會鎖住資源類,因此先打印sendEmail。
7. 1個靜態(tài)同步方法,1個普通同步方法,有1部手機 ,請問先打印郵件還是短信?
代碼:
//資源類 class Phone{public static synchronized void sendEmail() throws Exception {TimeUnit.SECONDS.sleep(2);System.out.println("sendEmail----------");}public synchronized void getSMS() {System.out.println("----------getSMS");}public void sayHello() {System.out.println("---hello---");} } //主方法 public class Lock_8 {public static void main(String[] args) throws Exception {Phone p=new Phone();new Thread(() ->{try {p.sendEmail();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}},"A").start();//強制讓線程A先執(zhí)行Thread.sleep(1000);new Thread(() ->{p.getSMS();},"B").start();} }結果:
解析:
- static方法鎖鎖的不是資源類,鎖的是類對象,也就是說,不管new了幾個,靜態(tài)同步方法的類對象都是一個;而普通同步方法鎖住的是new出來的對象。
- 所有的靜態(tài)同步方法用的也是同一把鎖——類對象本身,
這兩把鎖是兩個不同的對象,所以靜態(tài)同步方法與非靜態(tài)同步方法之間是不會有競態(tài)條件的。
但是一旦一個靜態(tài)同步方法獲取鎖后,其他的靜態(tài)同步方法都必須等待該方法釋放鎖后才能獲取鎖,
而不管是否是同一個實例對象。
8. 1個靜態(tài)同步方法,1個普通同步方法,有2部手機 ,請問先打印郵件還是短信?
代碼:
//資源類 class Phone{public static synchronized void sendEmail() throws Exception {TimeUnit.SECONDS.sleep(4);System.out.println("sendEmail----------");}public synchronized void getSMS() {System.out.println("----------getSMS");}public void sayHello() {System.out.println("---hello---");} } //主方法 public class Lock_8 {public static void main(String[] args) throws Exception {Phone p1=new Phone();Phone p2=new Phone();new Thread(() ->{try {p1.sendEmail();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}},"A").start();//強制讓線程A先執(zhí)行Thread.sleep(1000);new Thread(() ->{p2.getSMS();},"B").start();} }結果:
解析:
- P1鎖住的不是資源類,而是類對象。線程1鎖的是類對象,線程2鎖的是實例對象,雖然看似是一個對象,然而兩者不具備競態(tài)條件,因此修改main休眠時間之后就會發(fā)現getSMS先執(zhí)行。
8Lock總結
3. 補充:當前類的Class對象和當前類的實例對象分別是什么?
實例:
- 簡單理解,就是new,就是對類的實例化,創(chuàng)建這個類對應的實際對象,類只是對事物的描述,而實例化就相當于為這個描述新開辟了一塊內存,可以改變這塊區(qū)域里的各種屬性(成員變量),當然,也可以實例化多塊區(qū)域,只是不同的對象而已。
Class:
- 注意這里C大寫了,與類概念區(qū)分開,在java里,Class是一個實實在在的類,在包 java.lang 下,有這樣一個Class.java文件,它跟我們自己定義的類一樣,是一個實實在在的類,Class對象就是這個Class類的實例了。在Java里,所有的類的根源都是Object類,而Class也不例外,它是繼承自Object的一個特殊的類,它內部可以記錄類的成員、接口等信息,也就是在Java里,Class是一個用來表示類的類。(o(∩_∩)o 有點繞啊,抓住關鍵一點,Class是一個實實在在的類,可以為它創(chuàng)建實例,也就是本文后面提到的Class對象,也看叫做Class實例)。
java提供了下面幾種獲取到類的Class對象的方法:
1) 利用對象實例調用getClass()方法獲取該對象的Class實例;
2) 使用Class類的靜態(tài)方法forName(“包名+類名”),用類的名字獲取一個Class實例
3) 運用 類名.class 的方式來獲取Class實例;
我們知道java世界是運行在JVM之上的,我們編寫的類代碼,在經過編譯器編譯之后,會為每個類生成對應的.class文件,這個就是JVM可以加載執(zhí)行的字節(jié)碼。運行時期間,當我們需要實例化任何一個類時,JVM會首先嘗試看看在內存中是否有這個類,如果有,那么會直接創(chuàng)建類實例;如果沒有,那么就會根據類名去加載這個類,當加載一個類,或者當加載器(class loader)的defineClass()被JVM調用,便會為這個類產生一個Class對象(一個Class類的實例),用來表達這個類,該類的所有實例都共同擁有著這個Class對象,而且是唯一的。
總結
以上是生活随笔為你收集整理的Java多线程之8Lock问题解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java代码简化之lombok
- 下一篇: Java多线程之多线程之间按顺序调用