最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java 并發(fā)編程學(xué)習(xí)筆記之Synchronized簡介

 更新時(shí)間:2016年05月17日 09:09:21   作者:liuxiaopeng  
雖然多線程編程極大地提高了效率,但是也會(huì)帶來一定的隱患。比如說兩個(gè)線程同時(shí)往一個(gè)數(shù)據(jù)庫表中插入不重復(fù)的數(shù)據(jù),就可能會(huì)導(dǎo)致數(shù)據(jù)庫中插入了相同的數(shù)據(jù)。今天我們就來一起討論下線程安全問題,以及Java中提供了什么機(jī)制來解決線程安全問題。

一、Synchronized的基本使用

  Synchronized是Java中解決并發(fā)問題的一種最常用的方法,也是最簡單的一種方法。Synchronized的作用主要有三個(gè):(1)確保線程互斥的訪問同步代碼(2)保證共享變量的修改能夠及時(shí)可見(3)有效解決重排序問題。從語法上講,Synchronized總共有三種用法:

  (1)修飾普通方法

 ?。?)修飾靜態(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、對(duì)普通方法同步:

代碼段二:

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é)果如下,對(duì)靜態(tài)方法的同步本質(zhì)上是對(duì)類的同步(靜態(tài)方法本質(zhì)上是屬于類的方法,而不是對(duì)象上的方法),所以即使test和test2屬于不同的對(duì)象,但是它們都屬于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)入了對(duì)應(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 原理

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

package com.paddx.test.concurrent;

public class SynchronizedDemo {
  public void method() {
    synchronized (this) {
      System.out.println("Method 1 start");
    }
  }
}

反編譯結(jié)果:

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

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

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

3.如果其他線程已經(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所對(duì)應(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的對(duì)象來完成,其實(shí)wait/notify等方法也依賴于monitor對(duì)象,這就是為什么只有在同步的塊或者方法中才能調(diào)用wait/notify等方法,否則會(huì)拋出java.lang.IllegalMonitorStateException的異常的原因。

  我們?cè)賮砜匆幌峦椒椒ǖ姆淳幾g結(jié)果:

源代碼:

package com.paddx.test.concurrent;

public class SynchronizedMethod {
  public synchronized void method() {
    System.out.println("Hello World!");
  }
}

反編譯結(jié)果:

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

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

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

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

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

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

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

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

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

四 總結(jié)

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

相關(guān)文章

  • SpringBoot多環(huán)境日志配置方式

    SpringBoot多環(huán)境日志配置方式

    SpringBoot?默認(rèn)使用LogBack日志系統(tǒng),默認(rèn)情況下,SpringBoot項(xiàng)目的日志只會(huì)在控制臺(tái)輸入,本文給大家介紹SpringBoot多環(huán)境日志配置方式,需要的朋友可以參考下
    2024-08-08
  • DOM解析XML報(bào)錯(cuò)Content is not allowed in prolog解決方案詳解

    DOM解析XML報(bào)錯(cuò)Content is not allowed in prolog解決方案詳解

    這篇文章主要介紹了DOM解析XML報(bào)錯(cuò)解決方案詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 基于binarywang封裝的微信工具包生成二維碼

    基于binarywang封裝的微信工具包生成二維碼

    這篇文章主要介紹了基于binarywang封裝的微信工具包生成二維碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Java8 新特性之日期時(shí)間對(duì)象及一些其他特性

    Java8 新特性之日期時(shí)間對(duì)象及一些其他特性

    這篇文章主要介紹了Java8 新特性之日期時(shí)間對(duì)象及一些其他特性,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Spring Cloud Feign實(shí)現(xiàn)文件上傳下載的示例代碼

    Spring Cloud Feign實(shí)現(xiàn)文件上傳下載的示例代碼

    Feign框架對(duì)于文件上傳消息體格式并沒有做原生支持,需要集成模塊feign-form來實(shí)現(xiàn),本文就詳細(xì)的介紹一下如何使用,感興趣的可以了解一下
    2022-02-02
  • JavaWeb中的文件的上傳和下載

    JavaWeb中的文件的上傳和下載

    JavaWeb 文件的上傳和下載是指在Web應(yīng)用中實(shí)現(xiàn)用戶上傳文件到服務(wù)器和從服務(wù)器下載文件的功能,通過JavaWeb技術(shù),可以方便地實(shí)現(xiàn)文件的上傳和下載操作,提供更好的用戶體驗(yàn)和數(shù)據(jù)交互,需要的朋友可以參考下
    2023-10-10
  • Java數(shù)據(jù)結(jié)構(gòu)之堆(優(yōu)先隊(duì)列)的實(shí)現(xiàn)

    Java數(shù)據(jù)結(jié)構(gòu)之堆(優(yōu)先隊(duì)列)的實(shí)現(xiàn)

    堆(優(yōu)先隊(duì)列)是一種典型的數(shù)據(jù)結(jié)構(gòu),其形狀是一棵完全二叉樹,一般用于求解topk問題。本文將利用Java語言實(shí)現(xiàn)堆,感興趣的可以學(xué)習(xí)一下
    2022-05-05
  • Spring?Cloud?Ribbon?負(fù)載均衡使用策略示例詳解

    Spring?Cloud?Ribbon?負(fù)載均衡使用策略示例詳解

    Spring?Cloud?Ribbon?是基于Netflix?Ribbon?實(shí)現(xiàn)的一套客戶端負(fù)載均衡工具,Ribbon客戶端組件提供了一系列的完善的配置,如超時(shí),重試等,這篇文章主要介紹了Spring?Cloud?Ribbon?負(fù)載均衡使用策略示例詳解,需要的朋友可以參考下
    2023-03-03
  • MongoDB中ObjectId的誤區(qū)及引起的一系列問題

    MongoDB中ObjectId的誤區(qū)及引起的一系列問題

    這篇文章主要介紹了MongoDB中ObjectId的誤區(qū)及引起的一系列問題,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-12-12
  • 教你代碼中獲取當(dāng)前?JAR?包的存放位置

    教你代碼中獲取當(dāng)前?JAR?包的存放位置

    這篇文章主要介紹了如何獲取當(dāng)前JAR包的存放位置,要獲取當(dāng)前運(yùn)行的 JAR 包所存放的位置,可以使用 ProtectionDomain 和 CodeSource 類,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-08-08

最新評(píng)論

兴海县| 喀喇沁旗| 乐陵市| 武汉市| 马边| 南华县| 凌源市| 正宁县| 河津市| 常宁市| 武乡县| 铅山县| 潢川县| 新和县| 宜兰县| 高台县| 青海省| 聊城市| 长葛市| 潼关县| 星座| 仪陇县| 奉新县| 淮阳县| 贺兰县| 星座| 莱西市| 鞍山市| 宣城市| 凤冈县| 墨竹工卡县| 台前县| 奉新县| 姜堰市| 托里县| 丰城市| 漠河县| 桐柏县| 利川市| 贞丰县| 建始县|