前言:關(guān)于synchronized、wait、notify已經(jīng)notifyAll大家應(yīng)該不陌生,現(xiàn)在我大致說一下我的理解。

一:synchronized

synchronized中文解釋是同步,那么什么是同步呢,解釋就是程序中用于控制不同線程間操作發(fā)生相對(duì)順序的機(jī)制,通俗來講就是2點(diǎn),第一要有多線程,第二當(dāng)多個(gè)線程同時(shí)競(jìng)爭(zhēng)某個(gè)資源的時(shí)候會(huì)有先后順序。在java中有三種寫synchronized的方式

  • 第一種:

    • 寫在普通方法的前面,這種表示對(duì)實(shí)例對(duì)象加鎖。

  • 第二種:

    • 寫在靜態(tài)方法前面,這種表示對(duì)類對(duì)象加鎖

  • 第三種:

    • 寫在代碼塊中,鎖是Synchonized括號(hào)里配置的對(duì)象(可能是實(shí)例對(duì)象,也可能是類對(duì)象)

總體說來就2種,一種就是鎖實(shí)例對(duì)象,一種鎖類對(duì)象。

鎖實(shí)例對(duì)象就是當(dāng)多個(gè)線程同時(shí)操作這個(gè)實(shí)例對(duì)象的時(shí)候必須先獲取鎖,如果無法獲取鎖,則必須處于等待狀態(tài),而和鎖類對(duì)象區(qū)別是,當(dāng)多個(gè)線程同時(shí)操作的時(shí)候,任何以這個(gè)類對(duì)象實(shí)例化的對(duì)象都要獲取鎖才能操作。舉個(gè)簡(jiǎn)單例子

比如一個(gè)群人去打飯,只要是人就必須排隊(duì)等待,一個(gè)個(gè)的打飯。不管是誰,但是吃完飯之后把盤子送回原地,但是這個(gè)時(shí)候不同的人可能吃飯快慢不同,但是肯定先吃飯后送盤子。現(xiàn)在寫段代碼我們比對(duì)一下。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public class RunnableTest implements Runnable {    private synchronized  void testSyncMethod() {        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getId() + "testSyncMethod:" + i);
        }
    }    public void run() {
     testSyncMethod();
    }    public static void main(String[] args) {
        ExecutorService exec = Executors.newFixedThreadPool(2);
        RunnableTest rt = new RunnableTest();
        RunnableTest rt1 = new RunnableTest();
        exec.execute(rt);
        exec.execute(rt1);
        exec.shutdown();
    }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

按照我們的理論輸出結(jié)果肯定是無序排列的。如圖iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public class RunnableTest implements Runnable {    private void testSyncBlock() {        synchronized (RunnableTest.class) {            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getId()+"testSyncBlock:" + i);
            }
        }
    }    public void run() {
     testSyncBlock();
    }    public static void main(String[] args) {
        ExecutorService exec = Executors.newFixedThreadPool(2);
        RunnableTest rt = new RunnableTest();
        RunnableTest rt1 = new RunnableTest();
        exec.execute(rt);
        exec.execute(rt1);
        exec.shutdown();
    }
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

而這段代碼輸入結(jié)果肯定是有序的。如下iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

那么我們?cè)谒伎家粋€(gè)問題,如果類A有2個(gè)方法,如果我們?cè)谄渲幸粋€(gè)方法前面加入了synchronized,哪意味著我們別的線程調(diào)用這個(gè)類的另一個(gè)方法也需要獲取鎖才可以執(zhí)行,也是另一個(gè)方法只是讀,這樣一來性能就大大的降低,所以我們?cè)趯?shí)際開發(fā)中盡量少在方法前加入synchronized,那么我們應(yīng)該怎么做呢,既然是實(shí)際對(duì)象我們只需要加入一個(gè)類,鎖定此類,只需要讓類的一個(gè)方法進(jìn)行鎖定即可。ok下面代碼如下

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public class A {    private Object obj="123";    public  void a(){        synchronized (obj) {            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread() + "a:" + i);
            }
        }
    }    public void b(){            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread() + "b:" + i);
            }

    }
}public class B implements Runnable{    private A a;    public B(A a){        this.a=a;
    }    public void run() {
        a.b();
    }
}public class C implements Runnable {    private A a;    public C(A a){        this.a=a;
    }    public void run() {
        a.a();
    }
}public class E implements Runnable{    private A a;    public E(A a){        this.a=a;
    }    public void run() {
        a.a();
    }
}public class D {    public static void main(String[] args) {
        A a=new A();
        ExecutorService executorService= Executors.newCachedThreadPool();
        executorService.execute(new E((a)));
        executorService.execute(new B(a));
        executorService.execute(new C(a));
        executorService.shutdown();
    }
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

按照我們理論這段代碼執(zhí)行順序是第一個(gè)線程和第二個(gè)線程無序,第三個(gè)線程必須等待第一個(gè)線程執(zhí)行完畢才可以,測(cè)試結(jié)果也論證了我們的理論如下

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

二:wait、notify已經(jīng)notifyAll

wait、notify、notifyAll是Object對(duì)象的屬性,并不屬于線程。我們先解釋這三個(gè)的一個(gè)很重要的概念

wait:使持有該對(duì)象的線程把該對(duì)象的控制權(quán)交出去,然后處于等待狀態(tài)(這句話很重要,也就是說當(dāng)調(diào)用wait的時(shí)候會(huì)釋放鎖并處于等待的狀態(tài))

notify:通知某個(gè)正在等待這個(gè)對(duì)象的控制權(quán)的線程可以繼續(xù)運(yùn)行(這個(gè)就是獲取鎖,使自己的程序開始執(zhí)行,最后通過notify同樣去釋放鎖,并喚醒正在等待的線程)

notifyAll:會(huì)通知所有等待這個(gè)對(duì)象控制權(quán)的線程繼續(xù)運(yùn)行(和上面一樣,只不過是喚醒所有等待的線程繼續(xù)執(zhí)行)

這個(gè)就好了,從上面的解釋我們可以看出通過wait和notify可以做線程之間的通信,當(dāng)A線程處理完畢通知B線程執(zhí)行,B線程執(zhí)行完畢以后A線程可以繼續(xù)執(zhí)行。ok我們使用例子來說明。

 

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public class Temp {    int count=0;    public void waiter() throws InterruptedException {        synchronized (this) {
               System.out.println("等待");
               wait();
               System.out.println(this.count);
        }
    }    public void notifyer() throws InterruptedException {        synchronized (this){
            TimeUnit.SECONDS.sleep(1);
            System.out.println("喚醒");            for (int i=0;i<10;i++){
                System.out.println(Thread.currentThread()+"notifyer:"+i);
                count+=i;
            }
            notify();
        }
    }public class Waiter implements Runnable{    private Temp temp;    public Waiter(Temp temp){        this.temp=temp;
    }    public void run() {        try {
            temp.waiter();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}public class Notifyer implements Runnable{    private Temp temp;    public Notifyer(Temp temp){        this.temp=temp;
    }    public void run() {        try {
            temp.notifyer();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}   public static void main(String[] args) {
        Temp temp=new Temp();
        ExecutorService executorService= Executors.newCachedThreadPool();
        executorService.execute(new Waiter(temp));
        executorService.execute(new Notifyer(temp));
        executorService.shutdown();
    }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

其中在notify中加入休眠1s目的是讓線程waiter先執(zhí)行更能看明白

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

我們?cè)谂e一個(gè)例子,比如說我們經(jīng)常提到的客戶端請(qǐng)求和服務(wù)器響應(yīng),當(dāng)客戶端發(fā)送請(qǐng)求后就處于等待服務(wù)端的響應(yīng),而服務(wù)端會(huì)等待客戶端端請(qǐng)求然后響應(yīng)客戶端請(qǐng)求,下面我們看看怎么去寫代碼

首先我們寫一個(gè)對(duì)象Handler來專門處理客戶端和服務(wù)端的,對(duì)于客戶端有2個(gè)方法就是發(fā)送請(qǐng)求和等待服務(wù)端響應(yīng),對(duì)于服務(wù)端同樣2個(gè)方法那就是等待客戶端請(qǐng)求和響應(yīng)客戶端

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public class Handler {    private boolean isClientRequest=false;    public void sendRequest(){        synchronized (this){
            isClientRequest=true;            this.notifyAll();
        }
    }    public void waitResponse() throws InterruptedException {        synchronized (this){            while (isClientRequest){                this.wait();
            }
        }
    }    public void receiveRequest(){        synchronized (this) {
            isClientRequest = false;            this.notifyAll();
        }
    }    public void waitRequest() throws InterruptedException {        synchronized (this){            while (!isClientRequest){                this.wait();
            }
        }
    }
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

現(xiàn)在我們寫客戶端代碼,客戶端肯定先發(fā)送請(qǐng)求,但是先等待1s為了讓服務(wù)端處于等待的效果,發(fā)送請(qǐng)求后就處于等待狀態(tài)直到服務(wù)端的響應(yīng)

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public class Client implements Runnable {    private Handler handler;    public Client(Handler handler) {        this.handler = handler;
    }    public void run() {        try {            while (!Thread.interrupted()) {
                System.out.println("客戶端發(fā)送請(qǐng)求");
                TimeUnit.SECONDS.sleep(1);                this.handler.sendRequest();//第二步
                System.out.println("等待服務(wù)端的響應(yīng)");                this.handler.waitResponse();//第三步            }
        } catch (InterruptedException e) {

        }
        System.out.println("客戶端已經(jīng)完成請(qǐng)求");
    }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

然后我們寫服務(wù)端代碼,服務(wù)端首先處于等待狀態(tài),收到客戶端請(qǐng)求后立馬進(jìn)行處理,處理完畢之后再次等待客戶端的請(qǐng)求

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public class Server implements Runnable {    public Handler handler;    public Server(Handler handler) {        this.handler = handler;
    }    public void run() {        try {            while (!Thread.interrupted()) {
                System.out.println("等待客戶端請(qǐng)求");                this.handler.waitRequest();//第一步
                System.out.println("處理客戶端請(qǐng)求");
                TimeUnit.SECONDS.sleep(1);                this.handler.receiveRequest();//第四步            }
        } catch (InterruptedException e) {

        }
        System.out.println("服務(wù)端處理已經(jīng)完成");
    }
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

從上面我們預(yù)測(cè)肯定是等待客戶端請(qǐng)求,發(fā)送請(qǐng)求,等待響應(yīng),處理客戶端請(qǐng)求這樣循環(huán)的結(jié)果。如下

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

在說一下wait和sleep的區(qū)別

區(qū)別1:在wait期間對(duì)象鎖使釋放的

區(qū)別2:可以通過notify和notifyAll,或者玲命令到期,從wait中恢復(fù)執(zhí)行。如果wait不接受任何參數(shù),這種wait將無線的等待下去,直到線程收到notify或notifyall的消息

http://www.cnblogs.com/LipeiNet/p/6475851.html