大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節(jié)點(diǎn)LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 學(xué)習(xí)攻略 Java 并發(fā)編程:Synchronized 及其實(shí)現(xiàn)原理

Java 并發(fā)編程:Synchronized 及其實(shí)現(xiàn)原理

更新時(shí)間:2019-07-31 15:10:39 來源:動力節(jié)點(diǎn) 瀏覽2228次



一、Synchronized的基本使用


Synchronized是Java中解決并發(fā)問題的一種最常用的方法,也是最簡單的一種方法。


Synchronized的作用主要有三個(gè):


(1)確保線程互斥的訪問同步代碼


(2)保證共享變量的修改能夠及時(shí)可見


(3)有效解決重排序問題。


從語法上講,Synchronized總共有三種用法:


1、修飾普通方法


2、修飾靜態(tài)方法


3、修飾代碼塊


接下來我就通過幾個(gè)例子程序來說明一下這三種使用方式(為了便于比較,三段代碼除了Synchronized的使用方式不同以外,其他基本保持一致)。


1、沒有同步的情況:


package com.paddx.test.concurrent;


public class SynchronizedTest {

    public void method1(){

        System.out.println("Method 1 start");

        try {

            System.out.println("Method 1 execute");

            Thread.sleep(3000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 1 end");

    }


    public void method2(){

        System.out.println("Method 2 start");

        try {

            System.out.println("Method 2 execute");

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 2 end");

    }


    public static void main(String[] args) {

        final SynchronizedTest test = new SynchronizedTest();


        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method1();

            }

        }).start();


        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method2();

            }

        }).start();

    }

}


執(zhí)行結(jié)果如下,線程1和線程2同時(shí)進(jìn)入執(zhí)行狀態(tài),線程2執(zhí)行速度比線程1快,所以線程2先執(zhí)行完成,這個(gè)過程中線程1和線程2是同時(shí)執(zhí)行的。


Method 1 start

Method 1 execute

Method 2 start

Method 2 execute

Method 2 end

Method 1 end


2、對普通方法同步:


代碼段二:


package com.paddx.test.concurrent;


public class SynchronizedTest {

    public synchronized void method1(){

        System.out.println("Method 1 start");

        try {

            System.out.println("Method 1 execute");

            Thread.sleep(3000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 1 end");

    }


    public synchronized void method2(){

        System.out.println("Method 2 start");

        try {

            System.out.println("Method 2 execute");

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 2 end");

    }


    public static void main(String[] args) {

        final SynchronizedTest test = new SynchronizedTest();


        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method1();

            }

        }).start();


        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method2();

            }

        }).start();

    }

}


執(zhí)行結(jié)果如下,跟代碼段一比較,可以很明顯的看出,線程2需要等待線程1的method1執(zhí)行完成才能開始執(zhí)行method2方法。


Method 1 start

Method 1 execute

Method 1 end

Method 2 start

Method 2 execute

Method 2 end


3、靜態(tài)方法(類)同步


代碼段三:


package com.paddx.test.concurrent;

 

 public class SynchronizedTest {

     public static synchronized void method1(){

         System.out.println("Method 1 start");

         try {

             System.out.println("Method 1 execute");

             Thread.sleep(3000);

         } catch (InterruptedException e) {

             e.printStackTrace();

         }

         System.out.println("Method 1 end");

     }

 

     public static synchronized void method2(){

         System.out.println("Method 2 start");

         try {

             System.out.println("Method 2 execute");

             Thread.sleep(1000);

         } catch (InterruptedException e) {

             e.printStackTrace();

         }

         System.out.println("Method 2 end");

     }

 

     public static void main(String[] args) {

         final SynchronizedTest test = new SynchronizedTest();

         final SynchronizedTest test2 = new SynchronizedTest();

 

         new Thread(new Runnable() {

             @Override

             public void run() {

                 test.method1();

             }

         }).start();

 

         new Thread(new Runnable() {

             @Override

             public void run() {

                 test2.method2();

             }

         }).start();

     }

 }


執(zhí)行結(jié)果如下,對靜態(tài)方法的同步本質(zhì)上是對類的同步(靜態(tài)方法本質(zhì)上是屬于類的方法,而不是對象上的方法),所以即使test和test2屬于不同的對象,但是它們都屬于SynchronizedTest類的實(shí)例,所以也只能順序的執(zhí)行method1和method2,不能并發(fā)執(zhí)行。


Method 1 start

Method 1 execute

Method 1 end

Method 2 start

Method 2 execute

Method 2 end


4、代碼塊同步


代碼段四:


package com.paddx.test.concurrent;


public class SynchronizedTest {

    public void method1(){

        System.out.println("Method 1 start");

        try {

            synchronized (this) {

                System.out.println("Method 1 execute");

                Thread.sleep(3000);

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 1 end");

    }


    public void method2(){

        System.out.println("Method 2 start");

        try {

            synchronized (this) {

                System.out.println("Method 2 execute");

                Thread.sleep(1000);

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 2 end");

    }


    public static void main(String[] args) {

        final SynchronizedTest test = new SynchronizedTest();


        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method1();

            }

        }).start();


        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method2();

            }

        }).start();

    }

}


執(zhí)行結(jié)果如下,雖然線程1和線程2都進(jìn)入了對應(yīng)的方法開始執(zhí)行,但是線程2在進(jìn)入同步塊之前,需要等待線程1中同步塊執(zhí)行完成。


Method 1 start

Method 1 execute

Method 2 start

Method 1 end

Method 2 execute

Method 2 end




二、Synchronized 原理


如果對上面的執(zhí)行結(jié)果還有疑問,也先不用急,我們先來了解Synchronized的原理,再回頭上面的問題就一目了然了。我們先通過反編譯下面的代碼來看看Synchronized是如何實(shí)現(xiàn)對代碼塊進(jìn)行同步的:


package com.paddx.test.concurrent;


public class SynchronizedDemo {

    public void method() {

        synchronized (this) {

            System.out.println("Method 1 start");

        }

    }

}


反編譯結(jié)果:


QQ截圖20190731151551.png


關(guān)于這兩條指令的作用,我們直接參考JVM規(guī)范中描述:


monitorenter :


Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:


? If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.


? If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.


? If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.


這段話的大概意思為:


每個(gè)對象有一個(gè)監(jiān)視器鎖(monitor)。當(dāng)monitor被占用時(shí)就會處于鎖定狀態(tài),線程執(zhí)行monitorenter指令時(shí)嘗試獲取monitor的所有權(quán),過程如下:


如果monitor的進(jìn)入數(shù)為0,則該線程進(jìn)入monitor,然后將進(jìn)入數(shù)設(shè)置為1,該線程即為monitor的所有者。


如果線程已經(jīng)占有該monitor,只是重新進(jìn)入,則進(jìn)入monitor的進(jìn)入數(shù)加1.


如果其他線程已經(jīng)占用了monitor,則該線程進(jìn)入阻塞狀態(tài),直到monitor的進(jìn)入數(shù)為0,再重新嘗試獲取monitor的所有權(quán)。


monitorexit:


The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.


The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.


這段話的大概意思為:


執(zhí)行monitorexit的線程必須是objectref所對應(yīng)的monitor的所有者。


指令執(zhí)行時(shí),monitor的進(jìn)入數(shù)減1,如果減1后進(jìn)入數(shù)為0,那線程退出monitor,不再是這個(gè)monitor的所有者。其他被這個(gè)monitor阻塞的線程可以嘗試去獲取這個(gè) monitor 的所有權(quán)。 


通過這兩段描述,我們應(yīng)該能很清楚的看出Synchronized的實(shí)現(xiàn)原理,Synchronized的語義底層是通過一個(gè)monitor的對象來完成,其實(shí)wait/notify等方法也依賴于monitor對象,這就是為什么只有在同步的塊或者方法中才能調(diào)用wait/notify等方法,否則會拋出java.lang.IllegalMonitorStateException的異常的原因。


我們再來看一下同步方法的反編譯結(jié)果:


源代碼:


package com.paddx.test.concurrent;


public class SynchronizedMethod {

    public synchronized void method() {

        System.out.println("Hello World!");

    }

}



反編譯結(jié)果:


2.png


從反編譯的結(jié)果來看,方法的同步并沒有通過指令monitorenter和monitorexit來完成(理論上其實(shí)也可以通過這兩條指令來實(shí)現(xiàn)),不過相對于普通方法,其常量池中多了ACC_SYNCHRONIZED標(biāo)示符。JVM就是根據(jù)該標(biāo)示符來實(shí)現(xiàn)方法的同步的:當(dāng)方法調(diào)用時(shí),調(diào)用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標(biāo)志是否被設(shè)置,如果設(shè)置了,執(zhí)行線程將先獲取monitor,獲取成功之后才能執(zhí)行方法體,方法執(zhí)行完后再釋放monitor。在方法執(zhí)行期間,其他任何線程都無法再獲得同一個(gè)monitor對象。 其實(shí)本質(zhì)上沒有區(qū)別,只是方法的同步是一種隱式的方式來實(shí)現(xiàn),無需通過字節(jié)碼來完成。



三、運(yùn)行結(jié)果解釋


有了對Synchronized原理的認(rèn)識,再來看上面的程序就可以迎刃而解了。


1、代碼段2結(jié)果:


雖然method1和method2是不同的方法,但是這兩個(gè)方法都進(jìn)行了同步,并且是通過同一個(gè)對象去調(diào)用的,所以調(diào)用之前都需要先去競爭同一個(gè)對象上的鎖(monitor),也就只能互斥的獲取到鎖,因此,method1和method2只能順序的執(zhí)行。


2、代碼段3結(jié)果:


雖然test和test2屬于不同對象,但是test和test2屬于同一個(gè)類的不同實(shí)例,由于method1和method2都屬于靜態(tài)同步方法,所以調(diào)用的時(shí)候需要獲取同一個(gè)類上monitor(每個(gè)類只對應(yīng)一個(gè)class對象),所以也只能順序的執(zhí)行。


3、代碼段4結(jié)果:


對于代碼塊的同步實(shí)質(zhì)上需要獲取Synchronized關(guān)鍵字后面括號中對象的monitor,由于這段代碼中括號的內(nèi)容都是this,而method1和method2又是通過同一的對象去調(diào)用的,所以進(jìn)入同步塊之前需要去競爭同一個(gè)對象上的鎖,因此只能順序執(zhí)行同步塊。


四、總結(jié)


Synchronized是Java并發(fā)編程中最常用的用于保證線程安全的方式,其使用相對也比較簡單。但是如果能夠深入了解其原理,對監(jiān)視器鎖等底層知識有所了解,一方面可以幫助我們正確的使用Synchronized關(guān)鍵字,另一方面也能夠幫助我們更好的理解并發(fā)編程機(jī)制,有助我們在不同的情況下選擇更優(yōu)的并發(fā)策略來完成任務(wù)。對平時(shí)遇到的各種并發(fā)問題,也能夠從容的應(yīng)對。


更多java知識點(diǎn)內(nèi)容請點(diǎn)擊:http://www.dabaquan.cn/tutorial_java_se/


提交申請后,顧問老師會電話與您溝通安排學(xué)習(xí)

免費(fèi)課程推薦 >>
技術(shù)文檔推薦 >>
主站蜘蛛池模板: 欧美69xx | 一级一级毛片免费播放 | 国产99在线 | 精品国产91 | 国语毛片| 日韩在线一区二区 | 一区二区三区亚洲视频 | 亚洲成色 | 久一视频在线 | 中文字幕日本一区波多野不卡 | 亚洲欧美日韩高清在线看 | 国产高清不卡一区二区三区 | 精品999久久久久久中文字幕 | 亚洲欧美日韩国产综合久 | 国产区精品一区二区不卡中文 | 99久久99久久精品免费看子 | 男女一级毛片免费播放 | 欧美精品亚洲精品日韩一区 | 欧美一区二区影院 | 99视频在线观看视频一区 | 久久久精品久久久久特色影视 | 久久免费公开视频 | 国产精品一 | 国产日韩一区二区三区在线观看 | 一级毛片真人不卡免费播 | 久久一色本道亚洲 | 久久精品免视着国产成人 | 中文字幕久精品免费视频 | 国产在线观看不卡 | 色资源在线 | 国产成人精品日本亚洲语音1 | 久久厕所精品国产精品亚洲 | 日日操夜夜操狠狠操 | 色五月天天 | 国产婷婷一区二区三区 | 久久艹在线 | 97啪啪| 久久国产加勒比精品无码 | 欧美不卡影院 | 亚洲成人综合网站 | 久久99精品久久久久久噜噜丰满 |