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