多線程—Thread&Runable和synchronized

1. Thread & Runable

先不逼逼,上兩個(gè)例子再說(shuō)。

//栗子1
public class ThreadTest extends Thread {    
  @Override  
  public void run() {  
    //重寫run方法
}
public static void main(String[] args){  
  Thread t=new ThreadTest ();   
  t.start();  
}   
//栗子2
public class RunnableTest implements Runnable{  
    @Override  
    public void run() {  
       //重寫run方法
    }  
}  
public static void main(String[] args) {  
   RunnableTest r=new RunnableTest();  
   Thread t=new Thread(r);  
   t.start();  
}   

例1,繼承線程類Thread,創(chuàng)建子類ThreadTest,然后new一個(gè)子類對(duì)象,通過(guò)這個(gè)對(duì)象的start方法啟動(dòng)線程;
例2,實(shí)現(xiàn)Runnable接口創(chuàng)建子類,new一個(gè)子類對(duì)象,以這個(gè)子類對(duì)象為參數(shù),new出一個(gè)Thread對(duì)象,最后調(diào)用start方法啟動(dòng)線程。好吧,很簡(jiǎn)單對(duì)吧。直接上干貨。

2 Runable使用

使用Runnable更具靈活性,可以避免單繼承的局限,并且可以實(shí)現(xiàn)多個(gè)線程同時(shí)處理同一個(gè)資源,同樣還是看例子。

我們就以經(jīng)典的賣票系統(tǒng)講個(gè)故事吧。話說(shuō)有小T(Thread)和小R(Runnable)兩個(gè)票販子,他們手下各有兩個(gè)業(yè)務(wù)員,平常就干著倒賣活動(dòng)門票的活(線程執(zhí)行任務(wù))。有一天,他們各拿到了5張某演唱會(huì)的門票,兩人將售票的任務(wù)交給手下的業(yè)務(wù)員。

小T這邊的業(yè)務(wù)是這樣運(yùn)轉(zhuǎn)的(定義Thread類的子類,重寫run()方法,run()方法即為賣票任務(wù))

public class TicketThread extends Thread {
    private int ticket = 5;
    private String name;

    public TicketThread(String name) {
        this.name = name;
    }

    public void run() {
        for (int i = 0; i < 5; i++) {
            if (ticket > 0) {
                Log.e("T公司",name + "賣了一張票,編號(hào)為t" + (ticket--));
            }
        }
    }
}

業(yè)務(wù)員一大早就開(kāi)始賣票,很快就賣完了(實(shí)例化Thread的子類,調(diào)用start()方法啟動(dòng)該線程)

TicketThread t1 = new TicketThread("1號(hào)業(yè)務(wù)員");
TicketThread t2 = new TicketThread("2號(hào)業(yè)務(wù)員");

t1.start();
t2.start();

兩個(gè)業(yè)務(wù)員賣票結(jié)果如下:

T公司:1號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t5
T公司:2號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t5
T公司:1號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t4
T公司:1號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t3
T公司:2號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t4
T公司:1號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t2
T公司:2號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t3
T公司:1號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t1
T公司:2號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t2
T公司:2號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t1

而小R的業(yè)務(wù)流程則是這樣的(實(shí)現(xiàn)Runnable接口,重寫Runnable的run()方法)

public class TicketRunnable implements Runnable {
    private int ticket = 5;

    public void run() {
        for (int i = 0; i < 5; i++) {
            if (ticket > 0) {
                Log.e("R公司",Thread.currentThread().getName() + "賣了一張票,編號(hào)為r" + (ticket--));
            }
        }
    }
}

業(yè)務(wù)員同樣很快就把票賣完了(創(chuàng)建Thread子類的實(shí)例,將實(shí)現(xiàn)了Runnable接口的對(duì)象作為參數(shù)實(shí)例化Thread對(duì)象,調(diào)用start()方法啟動(dòng)線程)

TicketRunnable runnable = new TicketRunnable();
Thread r1 = new Thread(runnable, "1號(hào)業(yè)務(wù)員");
Thread r2 = new Thread(runnable, "2號(hào)業(yè)務(wù)員");

r1.start();
r2.start();

兩個(gè)業(yè)務(wù)員賣票結(jié)果如下:

R公司:1號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t5
R公司:2號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t4
R公司:1號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t3
R公司:2號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t2
R公司:1號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為t1

事畢,兩人決定吃個(gè)飯慶祝一下。一番交流后,小R很驚訝小T為啥可以將同樣的票多賣一倍的錢,小T得意地分享了自己的賣票心得:

“因?yàn)槲覍⑵睆?fù)印了一份,這樣我兩個(gè)小弟就可以各拿一份票去兜售了(通過(guò)繼承Thread的方式,當(dāng)新創(chuàng)建一個(gè)線程啟動(dòng)時(shí),其綁定的任務(wù)同樣也新建了一份,這樣他們的任務(wù)是相互獨(dú)立的,自然也無(wú)法實(shí)現(xiàn)資源共享了)”

小R聽(tīng)后感嘆不已:

“竟然還有這種操作?我耿直地將票交給兩個(gè)業(yè)務(wù)員就算了?。▽?shí)現(xiàn)Runnable接口的方式,因?yàn)槎际怯猛粋€(gè)Runnable對(duì)象創(chuàng)建的線程,因此多線程實(shí)際上執(zhí)行的是同一個(gè)任務(wù),這樣也就共享了資源)”
接著小R又問(wèn)小T:“你這么干,事后不怕買到假票的人來(lái)找你?” 小T聽(tīng)后不以為然道:

“怕啥,我們不都是干完一票就跑路的么(線程執(zhí)行完run()方法后會(huì)自行銷毀)”
小R:“我從未見(jiàn)過(guò)有如此厚顏無(wú)恥之人......”

3.1 synchronized使用解析-同步方法(非靜態(tài))

小R(Runnable)因?yàn)檎\(chéng)信經(jīng)營(yíng),生意越來(lái)越好了,于是小R便多招了一個(gè)業(yè)務(wù)員。某日,在售出10張門票后不久,小R就收到了顧客的投訴,說(shuō)他們買到了假票。小R懷疑是自己的手下動(dòng)了手腳,便展開(kāi)了調(diào)查:
當(dāng)時(shí)的業(yè)務(wù)流程如下

private class SellTask {
    private int ticket = 10;
    public void sellTicket(){
        if (ticket > 0) {
            try{
                Thread.sleep(500);
                Log.e("R公司",Thread.currentThread().getName() + "賣了一張票,編號(hào)為r" + (ticket--));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

public class TicketRunnable implements Runnable {
    SellTask sellTask;
    public TicketRunnable(SellTask sellTask){
        this.sellTask = sellTask;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            sellTask.sellTicket();
        }
    }
}

票交由3個(gè)業(yè)務(wù)員去賣

SellTask sellTask = new SellTask();
TicketRunnable runnable = new TicketRunnable(sellTask);
Thread r1 = new Thread(runnable, "1號(hào)業(yè)務(wù)員");
Thread r2 = new Thread(runnable, "2號(hào)業(yè)務(wù)員");
Thread r3 = new Thread(runnable, "3號(hào)業(yè)務(wù)員");

r1.start();
r2.start();
r3.start();

調(diào)查發(fā)現(xiàn),出現(xiàn)了多個(gè)業(yè)務(wù)員售出編號(hào)相同的票的情況

R公司:1號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為r10
R公司:3號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為r9
R公司:2號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為r8
R公司:1號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為r7
R公司:2號(hào)業(yè)務(wù)員賣了一張票,編號(hào)為r7
...

進(jìn)一步調(diào)查后得知,出現(xiàn)這樣的情況是因?yàn)闃I(yè)務(wù)員答應(yīng)了賣給顧客某編號(hào)的票,并收取了訂金,回頭拿票時(shí)才發(fā)現(xiàn)哥幾個(gè)賣得是同一張票(多個(gè)線程先后操作共享數(shù)據(jù)造成數(shù)據(jù)錯(cuò)誤),沒(méi)辦法只能自己復(fù)制一張給顧客企圖蒙混過(guò)關(guān)。小R不知道該怎么約束自己的手下,遂公開(kāi)招聘能解決問(wèn)題的人。

這天,一位自稱synchronized的男人前來(lái)應(yīng)聘。小R問(wèn)道:“s先生有何高見(jiàn)啊?”s先生淡定地喝了口茶,答道:

“你現(xiàn)在的業(yè)務(wù)流程不太可靠(線程不安全),讓我來(lái)統(tǒng)一管理整個(gè)售票業(yè)務(wù),每一張票的出售都需經(jīng)過(guò)我的審批,一張票賣完后業(yè)務(wù)員才能來(lái)我這再次申請(qǐng)拿票出售,這樣每張票都只能由一個(gè)業(yè)務(wù)員進(jìn)行出售,問(wèn)題也自然解決了(在Java中每一個(gè)對(duì)象都有一個(gè)內(nèi)部鎖,當(dāng)使用synchronized關(guān)鍵字聲明某個(gè)方法時(shí),該方法將受到對(duì)象鎖的保護(hù),這樣一次就只能有一個(gè)線程可以進(jìn)入該方法并獲得該對(duì)象的鎖,其他線程要想調(diào)用該方法,只能排隊(duì)等待。當(dāng)獲得鎖的線程執(zhí)行完該方法并釋放對(duì)象鎖后,別的線程才可拿到鎖進(jìn)入該方法)?!?/p>

小R聽(tīng)后覺(jué)得這方法不錯(cuò),便讓s先生來(lái)試試。這次依然是要出售10張票,業(yè)務(wù)流程經(jīng)過(guò)s先生改進(jìn)后如下

private class SellTask {
    private int ticket = 10;
    public synchronized void sellTicket(){//使用synchronized聲明sellTicket方法
        if (ticket > 0) {
            try{
                Thread.sleep(500);
                Log.e("R公司",Thread.currentThread().getName() + "賣了一張票,編號(hào)為r" + (ticket--));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

與之前的SellTask類相比,該類中使用了synchronized聲明sellTicket()方法。如此一來(lái),問(wèn)題果然解決了,小R懸著的心也終于放了下來(lái)。

3.2 同一個(gè)對(duì)象內(nèi)使用多個(gè)同步方法

某日,小R又開(kāi)始向s先生抱怨起來(lái):“我那幫二愣子手下啊,每次進(jìn)我辦公室匯報(bào)工作都是亂糟糟的,讓他們按順序一個(gè)個(gè)來(lái)就是不聽(tīng),s先生覺(jué)得該如何管管他們???”s先生依然淡定地抿了口茶,說(shuō)道:“不急,容我先看看他們是怎么匯報(bào)的?!毙便依著s先生的意思安排了兩個(gè)手下過(guò)來(lái)匯報(bào)工作。

private class ReportTask {
    public void report1(){
        Log.e("R公司","1號(hào)業(yè)務(wù)員" + "進(jìn)辦公室");
        try{
            Log.e("R公司","1號(hào)業(yè)務(wù)員" + "開(kāi)始匯報(bào)");
            Thread.sleep(1000);

        }catch (Exception e){
            e.printStackTrace();
        }
        Log.e("R公司","1號(hào)業(yè)務(wù)員" + "匯報(bào)完畢");
        Log.e("R公司","1號(hào)業(yè)務(wù)員" + "出辦公室");
    }

    public void report2(){
        Log.e("R公司","2號(hào)業(yè)務(wù)員" + "進(jìn)辦公室");
        try{
            Log.e("R公司","2號(hào)業(yè)務(wù)員" + "開(kāi)始匯報(bào)");
            Thread.sleep(1000);

        }catch (Exception e){
            e.printStackTrace();
        }
        Log.e("R公司","2號(hào)業(yè)務(wù)員" + "匯報(bào)完畢");
        Log.e("R公司","2號(hào)業(yè)務(wù)員" + "出辦公室");
    }
}

public class ReportRunnable1 implements Runnable {
    ReportTask task;
    public ReportRunnable1(ReportTask task){
        this.task = task;
    }

    public void run() {
        task.report1();
    }
}

public class ReportRunnable2 implements Runnable {
    ReportTask task;
    public ReportRunnable2(ReportTask task){
        this.task = task;
    }

    public void run() {
        task.report2();
    }
}

不一會(huì)兒,兩個(gè)手下前后腳進(jìn)了辦公室,開(kāi)始匯報(bào)

ReportTask reportTask = new ReportTask();
ReportRunnable1 runnable1 = new ReportRunnable1(reportTask);
ReportRunnable2 runnable2 = new ReportRunnable2(reportTask);

Thread r1 = new Thread(runnable1);
Thread r2 = new Thread(runnable2);
r1.start();
r2.start();

這倆二愣子手下是這樣匯報(bào)工作的

R公司:1號(hào)業(yè)務(wù)員進(jìn)辦公室
R公司:1號(hào)業(yè)務(wù)員開(kāi)始匯報(bào)
R公司:2號(hào)業(yè)務(wù)員進(jìn)辦公室
R公司:2號(hào)業(yè)務(wù)員開(kāi)始匯報(bào)
R公司:1號(hào)業(yè)務(wù)員匯報(bào)完畢
R公司:1號(hào)業(yè)務(wù)員出辦公室
R公司:2號(hào)業(yè)務(wù)員匯報(bào)完畢
R公司:2號(hào)業(yè)務(wù)員出辦公室

小R揉了揉腦袋,嘆氣道:“唉,他們就是這樣匯報(bào)的,每次他們一起講的時(shí)候我都不知該聽(tīng)誰(shuí)的?!眘先生哈哈一笑,道:

“這個(gè)不難解決,下次他們?cè)賮?lái)匯報(bào),進(jìn)來(lái)第一個(gè)人我就把門鎖了,讓下一個(gè)在門外等,等第一個(gè)講完了我再放第二個(gè)進(jìn)來(lái)就行了(當(dāng)一個(gè)線程訪問(wèn)對(duì)象的某個(gè)synchronized同步方法時(shí),其他線程對(duì)對(duì)象中所有其它synchronized同步方法的訪問(wèn)將被阻塞)”

小R聽(tīng)后深以為然,便又安排剛剛那兩個(gè)業(yè)務(wù)員過(guò)來(lái)重新匯報(bào)一次,這次由s先生親自守門

private class ReportTask {
    public synchronized void report1(){
        Log.e("R公司","1號(hào)業(yè)務(wù)員" + "進(jìn)辦公室");
        try{
            Log.e("R公司","1號(hào)業(yè)務(wù)員" + "開(kāi)始匯報(bào)");
            Thread.sleep(1000);

        }catch (Exception e){
            e.printStackTrace();
        }
        Log.e("R公司","1號(hào)業(yè)務(wù)員" + "匯報(bào)完畢");
        Log.e("R公司","1號(hào)業(yè)務(wù)員" + "出辦公室");
    }

    public synchronized void report2(){
        Log.e("R公司","2號(hào)業(yè)務(wù)員" + "進(jìn)辦公室");
        try{
            Log.e("R公司","2號(hào)業(yè)務(wù)員" + "開(kāi)始匯報(bào)");
            Thread.sleep(1000);

        }catch (Exception e){
            e.printStackTrace();
        }
        Log.e("R公司","2號(hào)業(yè)務(wù)員" + "匯報(bào)完畢");
        Log.e("R公司","2號(hào)業(yè)務(wù)員" + "出辦公室");
    }
}

不一會(huì)兒,兩個(gè)業(yè)務(wù)員又來(lái)了,這次的結(jié)果令小R非常滿意

R公司:1號(hào)業(yè)務(wù)員進(jìn)辦公室
R公司:1號(hào)業(yè)務(wù)員開(kāi)始匯報(bào)
R公司:1號(hào)業(yè)務(wù)員匯報(bào)完畢
R公司:1號(hào)業(yè)務(wù)員出辦公室
R公司:2號(hào)業(yè)務(wù)員進(jìn)辦公室
R公司:2號(hào)業(yè)務(wù)員開(kāi)始匯報(bào)
R公司:2號(hào)業(yè)務(wù)員匯報(bào)完畢
R公司:2號(hào)業(yè)務(wù)員出辦公室

看著小R這么開(kāi)心,s先生不禁潑起了冷水:

“你別高興得太早,你窗戶可沒(méi)鎖呢,說(shuō)不定你那幫二愣子手下進(jìn)不了門就從窗戶爬進(jìn)來(lái)了(當(dāng)一個(gè)線程訪問(wèn)對(duì)象的某個(gè)synchronized同步方法時(shí),另一個(gè)線程仍然可以訪問(wèn)該對(duì)象中的非synchronized同步方法)?!?/p>

果然,之后的某次工作匯報(bào)中,這樣的事就發(fā)生了

private class ReportTask {
    public synchronized void report1(){
        Log.e("R公司","1號(hào)業(yè)務(wù)員" + "進(jìn)辦公室");
        try{
            Log.e("R公司","1號(hào)業(yè)務(wù)員" + "開(kāi)始匯報(bào)");
            Thread.sleep(1000);

        }catch (Exception e){
            e.printStackTrace();
        }
        Log.e("R公司","1號(hào)業(yè)務(wù)員" + "匯報(bào)完畢");
        Log.e("R公司","1號(hào)業(yè)務(wù)員" + "出辦公室");
    }

    public synchronized void report2(){
        Log.e("R公司","2號(hào)業(yè)務(wù)員" + "進(jìn)辦公室");
        try{
            Log.e("R公司","2號(hào)業(yè)務(wù)員" + "開(kāi)始匯報(bào)");
            Thread.sleep(1000);

        }catch (Exception e){
            e.printStackTrace();
        }
        Log.e("R公司","2號(hào)業(yè)務(wù)員" + "匯報(bào)完畢");
        Log.e("R公司","2號(hào)業(yè)務(wù)員" + "出辦公室");
    }

    public void report3(){
        Log.e("R公司","3號(hào)業(yè)務(wù)員" + "進(jìn)辦公室");
        try{
            Log.e("R公司","3號(hào)業(yè)務(wù)員" + "開(kāi)始匯報(bào)");
            Thread.sleep(1000);

        }catch (Exception e){
            e.printStackTrace();
        }
        Log.e("R公司","3號(hào)業(yè)務(wù)員" + "匯報(bào)完畢");
        Log.e("R公司","3號(hào)業(yè)務(wù)員" + "出辦公室");
    }
}

//線程啟動(dòng)代碼略...

report3()是沒(méi)有修飾的,當(dāng)一個(gè)線程訪問(wèn)對(duì)象的某個(gè)synchronized同步方法時(shí),另一個(gè)線程仍然可以訪問(wèn)該對(duì)象中的非synchronized修飾的方法report3()。

3.3 synchronized同步代碼塊

是日,好友小T前來(lái)拜訪小R,卻看見(jiàn)小R的辦公室門窗緊閉,幾個(gè)業(yè)務(wù)員在門外排著隊(duì)。小T十分疑惑,遂敲門招呼小R讓他開(kāi)門,然而卻沒(méi)有得到任何回應(yīng)。沒(méi)辦法,小T只能跟著業(yè)務(wù)員在辦公室外面等了

private class ReportTask {
    public void report1(){
        synchronized(this){
            Log.e("R公司","1號(hào)業(yè)務(wù)員" + "進(jìn)辦公室");
            try{
                Log.e("R公司","1號(hào)業(yè)務(wù)員" + "開(kāi)始匯報(bào)");
                Thread.sleep(1000);

            }catch (Exception e){
                e.printStackTrace();
            }
            Log.e("R公司","1號(hào)業(yè)務(wù)員" + "匯報(bào)完畢");
            Log.e("R公司","1號(hào)業(yè)務(wù)員" + "出辦公室");
        }
    }

    public void report2(){
        synchronized(this){
            Log.e("R公司","2號(hào)業(yè)務(wù)員" + "進(jìn)辦公室");
            try{
                Log.e("R公司","2號(hào)業(yè)務(wù)員" + "開(kāi)始匯報(bào)");
                Thread.sleep(1000);

            }catch (Exception e){
                e.printStackTrace();
            }
            Log.e("R公司","2號(hào)業(yè)務(wù)員" + "匯報(bào)完畢");
            Log.e("R公司","2號(hào)業(yè)務(wù)員" + "出辦公室");
        }
    }

    public void report3(){
        synchronized(this){
            Log.e("R公司","3號(hào)業(yè)務(wù)員" + "進(jìn)辦公室");
            try{
                Log.e("R公司","3號(hào)業(yè)務(wù)員" + "開(kāi)始匯報(bào)");
                Thread.sleep(1000);

            }catch (Exception e){
                e.printStackTrace();
            }
            Log.e("R公司","3號(hào)業(yè)務(wù)員" + "匯報(bào)完畢");
            Log.e("R公司","3號(hào)業(yè)務(wù)員" + "出辦公室");
        }
    }

    public void report4(){
        synchronized (this){
            Log.e("R公司","小T" + "進(jìn)辦公室");
        }

    }
}

//省略線程啟動(dòng)部分代碼

好不容易等到門開(kāi)了,一個(gè)業(yè)務(wù)員走了出來(lái),小T便一閃身溜了進(jìn)去,門立刻被小R鎖上了?!澳愀缮赌?,差點(diǎn)就夾到我了!”小T抱怨道。小R不好意思笑笑,說(shuō)道:“原來(lái)是小T啊,你坐你坐,待會(huì)再向你解釋,我先放下個(gè)業(yè)務(wù)員進(jìn)來(lái)...”

R公司:1號(hào)業(yè)務(wù)員進(jìn)辦公室
R公司:1號(hào)業(yè)務(wù)員開(kāi)始匯報(bào)
R公司:1號(hào)業(yè)務(wù)員匯報(bào)完畢
R公司:1號(hào)業(yè)務(wù)員出辦公室
R公司:2號(hào)業(yè)務(wù)員進(jìn)辦公室
R公司:2號(hào)業(yè)務(wù)員開(kāi)始匯報(bào)
R公司:2號(hào)業(yè)務(wù)員匯報(bào)完畢
R公司:2號(hào)業(yè)務(wù)員出辦公室
R公司:小T進(jìn)辦公室
R公司:3號(hào)業(yè)務(wù)員進(jìn)辦公室
R公司:3號(hào)業(yè)務(wù)員開(kāi)始匯報(bào)
R公司:3號(hào)業(yè)務(wù)員匯報(bào)完畢
R公司:3號(hào)業(yè)務(wù)員出辦公室

好不容易應(yīng)付完所有業(yè)務(wù)員,小R向小T解釋了來(lái)龍去脈,并拿出了一把鑰匙交給小T

private class ReportTask {
    public void report1(){
        synchronized(this){
        //省略部分代碼...
        }
    }

    public void report2(){
        synchronized(this){
        //省略部分代碼...
        }
    }

    public void report3(){
        synchronized(this){
        //省略部分代碼...
        }
    }

    private String window = "window";
    public void report4(){
        synchronized (window){
            Log.e("R公司","小T" + "進(jìn)辦公室");
        }
    }
}

"s先生自然有應(yīng)對(duì)這種情況的妙計(jì),這把鑰匙可以打開(kāi)窗戶(持有window對(duì)象的內(nèi)置鎖),你以后可以從窗戶直接爬進(jìn)來(lái),不用在門外排隊(duì)(synchronized (obj){}同步代碼塊和用synchronized聲明方法的作用基本一致,都是對(duì)synchronized作用范圍內(nèi)的代碼進(jìn)行加鎖保護(hù),其區(qū)別在于synchronized同步代碼塊使用更加靈活、輕巧,synchronized (obj){}括號(hào)內(nèi)的對(duì)象參數(shù)即為該代碼塊持有鎖的對(duì)象。例如上述例子中,前面三個(gè)report方法中的同步代碼塊持有鎖的對(duì)象為ReportTask的實(shí)例對(duì)象,而report4方法中的同步代碼塊持有鎖的對(duì)象則為window。因?yàn)閷?duì)象都有自己的對(duì)象鎖,只能保護(hù)屬于自己的同步代碼塊或同步方法,所以即使其他線程進(jìn)入前三個(gè)方法的同步代碼塊中并獲得相應(yīng)對(duì)象的鎖,也不會(huì)阻塞進(jìn)入report4方法的線程執(zhí)行其中的同步代碼)。"

拿到鑰匙后,小T再也不用和業(yè)務(wù)員一起在門外排隊(duì)了

3.4 區(qū)分靜態(tài)同步方法和非靜態(tài)同步方法

了解了上述知識(shí)后,我們回過(guò)頭再來(lái)理解同步方法和靜態(tài)同步方法的區(qū)別。從持有鎖的對(duì)象的不同我們可以將synchronized同步代碼的方式分為兩大派系:

  1. synchronized聲明非靜態(tài)方法、同步代碼塊的synchronized (this){}和synchronized (非this對(duì)象){}這三者持有鎖的對(duì)象為實(shí)例對(duì)象(類的實(shí)例對(duì)象可以有很多個(gè)),線程想要執(zhí)行該synchronized作用范圍內(nèi)的同步代碼,需獲得對(duì)象鎖。
public class SynchronizedTest {
    public synchronized void test1(){
        //持有鎖的對(duì)象為SynchronizedTest的實(shí)例對(duì)象
    }

    public void test2(){
        synchronized (this){
            //持有鎖的對(duì)象為SynchronizedTest的實(shí)例對(duì)象
        }
    }

    private String obj = "obj";
    public void test3(){
        synchronized (obj){
            //持有鎖的對(duì)象為obj
        }
    }
}
  1. synchronized聲明靜態(tài)方法以及同步代碼塊的synchronized (類.class){}這兩者持有鎖的對(duì)象為Class對(duì)象(每個(gè)類只有一個(gè)Class對(duì)象,而Class對(duì)象是Java類編譯后生成的.class文件,它包含了與類有關(guān)的信息),線程想要執(zhí)行該synchronized作用范圍內(nèi)的同步代碼,需獲得類鎖
public class SynchronizedTest {
    public static synchronized void test4(){
        //持有鎖的對(duì)象為SynchronizedTest的Class對(duì)象(SynchronizedTest.class)
    }

    public void test5(){
        synchronized (SynchronizedTest.class){
            //持有鎖的對(duì)象為SynchronizedTest的Class對(duì)象(SynchronizedTest.class)
        }
    }
}

有關(guān)實(shí)例對(duì)象和Class對(duì)象的詳細(xì)知識(shí)大家可以繼續(xù)在網(wǎng)上查找資料進(jìn)行深挖,這里就不贅述了,總之我們要記住的一點(diǎn)是

若synchronized同步方法(代碼塊)持有鎖的對(duì)象不同,則多線程執(zhí)行相應(yīng)的同步代碼時(shí)互不干擾;若相同,則獲得該對(duì)象鎖的線程先執(zhí)行同步代碼,其他訪問(wèn)同步代碼的線程會(huì)被阻塞并等待鎖的釋放

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載Github:github.com/AnliaLee首發(fā)地址:A...
    Anlia閱讀 2,378評(píng)論 2 9
  • 一、進(jìn)程和線程 進(jìn)程 進(jìn)程就是一個(gè)執(zhí)行中的程序?qū)嵗?,每個(gè)進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間,一個(gè)進(jìn)程中可以有多個(gè)線程。...
    阿敏其人閱讀 2,698評(píng)論 0 13
  • 1、Synchronized關(guān)鍵字 1、方法中的變量不存在非線程安全問(wèn)題,都是線程安全的。 2、兩個(gè)線程訪問(wèn)同一個(gè)...
    AI喬治閱讀 1,550評(píng)論 1 7
  • 本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等。 首先講...
    李欣陽(yáng)閱讀 2,594評(píng)論 1 15
  • Java多線程學(xué)習(xí) [-] 一擴(kuò)展javalangThread類 二實(shí)現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 3,106評(píng)論 1 18

友情鏈接更多精彩內(nèi)容