欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Java多線程編程中的線程同步方法

 更新時(shí)間:2016年05月10日 08:59:48   作者:arthinking  
這篇文章主要介紹了Java多線程編程中的線程同步方法,使用synchronized關(guān)鍵字創(chuàng)建線程同步方法是實(shí)現(xiàn)線程同步的關(guān)鍵,需要的朋友可以參考下

1、多線程的同步:
1.1、同步機(jī)制:
在多線程中,可能有多個(gè)線程試圖訪問(wèn)一個(gè)有限的資源,必須預(yù)防這種情況的發(fā)生。所以引入了同步機(jī)制:在線程使用一個(gè)資源時(shí)為其加鎖,這樣其他的線程便不能訪問(wèn)那個(gè)資源了,直到解鎖后才可以訪問(wèn)。

1.2、共享成員變量的例子:
成員變量與局部變量:
成員變量:

如果一個(gè)變量是成員變量,那么多個(gè)線程對(duì)同一個(gè)對(duì)象的成員變量進(jìn)行操作,這多個(gè)線程是共享一個(gè)成員變量的。

局部變量:

如果一個(gè)變量是局部變量,那么多個(gè)線程對(duì)同一個(gè)對(duì)象進(jìn)行操作,每個(gè)線程都會(huì)有一個(gè)該局部變量的拷貝。他們之間的局部變量互不影響。

下面舉例說(shuō)明:
實(shí)現(xiàn)了Runnable的線程類:

class MyThread3 implements Runnable{

 //兩個(gè)線程操作同一個(gè)對(duì)象,共享成員變量
 //int i;
 @Override
 public void run() {
  //兩個(gè)線程操作同一個(gè)對(duì)象,各自保存局部變量的拷貝
  int i = 0;
  while(i<100){
   System.out.println(i);
   i++;
   try {
    Thread.sleep(100);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
 }
}

在main方法中用兩個(gè)線程操作同一個(gè)對(duì)象:

public static void main(String[] args) {

 MyThread3 myThread = new MyThread3();
 //下面兩個(gè)線程對(duì)同一個(gè)對(duì)象(Runnable的實(shí)現(xiàn)類對(duì)象)進(jìn)行操作
 Thread thread = new Thread(myThread);
 Thread thread2 = new Thread(myThread);
 //各自保存局部變量的拷貝,互不影響,輸出200個(gè)數(shù)字
 thread.start();
 thread2.start();
}

這里如果把i變成成員變量,則輸出100個(gè)數(shù)字。

1.3、共享資源導(dǎo)致的讀取錯(cuò)誤
下面舉個(gè)例子,兩個(gè)線程共用一個(gè)Number對(duì)象,通過(guò)Number類的getNumber方法獲取數(shù)據(jù),讀取數(shù)據(jù)并改寫(xiě)時(shí),發(fā)現(xiàn)了重復(fù)讀操作:

首先創(chuàng)建一個(gè)Number類:

class Number{
 private int number = 10;
 public String getNumber(int i){
  if(number > 0){
   try {
    Thread.sleep(100);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   number -= i;
   return "取出"+i+"成功,剩余數(shù)量:"+number;
  }
  return "取出"+i+"失敗,剩余數(shù)量:"+number;
 }
}

線程類,在線程類中的私有屬性包含了Number類的引用:

class MyThread4 extends Thread{

 //兩個(gè)線程操作同一個(gè)對(duì)象,共享成員變量
 Number number;
 public MyThread4(Number number){
  this.number = number;
 }
 @Override
 public void run() {
  System.out.println(number.getNumber(8));
 }
}

在main函數(shù)中創(chuàng)建兩個(gè)線程類,包含了同一個(gè)Number類實(shí)例的引用:

public static void main(String[] args) {

 Number number = new Number();
 //兩個(gè)線程操作同一個(gè)對(duì)象,共享對(duì)象number的成員變量number
 MyThread4 myThread = new MyThread4(number);
 MyThread4 myThread2 = new MyThread4(number);
 myThread.start();
 myThread2.start();
}

這樣,當(dāng)?shù)谝粋€(gè)線程讀取Number中的number變量時(shí)先保存下來(lái)再休眠0.1秒,然后第二個(gè)線程再讀取number變量并保存,此時(shí)兩個(gè)線程保存了同樣的數(shù)字,在修改時(shí),也就導(dǎo)致修改了同一個(gè)數(shù)字兩次。

2、同步機(jī)制的實(shí)現(xiàn):

在多線程并發(fā)編程中Synchronized一直是元老級(jí)角色,很多人都會(huì)稱呼它為重量級(jí)鎖,但是隨著Java SE1.6對(duì)Synchronized進(jìn)行了各種優(yōu)化之后,有些情況下它并不那么重了”
Java中的每一個(gè)對(duì)象都可以作為鎖。

對(duì)于同步方法,鎖是當(dāng)前實(shí)例對(duì)象。
對(duì)于靜態(tài)同步方法,鎖是當(dāng)前對(duì)象的Class對(duì)象。
對(duì)于同步方法塊,鎖是Synchonized括號(hào)里配置的對(duì)象。
當(dāng)一個(gè)線程試圖訪問(wèn)同步代碼塊時(shí),它首先必須得到鎖,退出或拋出異常時(shí)必須釋放鎖。
2.1、使用synchronized關(guān)鍵字創(chuàng)建synchronized方法:
使用synchronized關(guān)鍵字,該關(guān)鍵字修飾的方法叫做同步方法。

Java中每個(gè)對(duì)象都有一個(gè)鎖或者稱為監(jiān)視器,當(dāng)訪問(wèn)某個(gè)對(duì)象的synchronized方法時(shí),表示將該對(duì)象上鎖,而不僅僅是為該方法上鎖。

這樣如果一個(gè)對(duì)象的synchronized方法被某個(gè)線程執(zhí)行時(shí),其他線程無(wú)法訪問(wèn)該對(duì)象的任何synchronized方法(但是可以調(diào)用其他非synchronized的方法)。直至該synchronized方法執(zhí)行完。

靜態(tài)的synchronized方法調(diào)用情況:
當(dāng)調(diào)用一個(gè)對(duì)象的靜態(tài)synchronized方法時(shí),它鎖定的并不是synchronized方法所在的對(duì)象,而是synchronized方法所在對(duì)象對(duì)應(yīng)的Class對(duì)象。這樣,其他線程就不能調(diào)用該類的其他靜態(tài)synchronized方法了,但是可以調(diào)用非靜態(tài)的synchronized方法。

結(jié)論:執(zhí)行靜態(tài)synchronized方法鎖方法所在對(duì)象,執(zhí)行非靜態(tài)synchronized方法鎖方法所在對(duì)象對(duì)應(yīng)的Class對(duì)象。

下面是多線程調(diào)用靜態(tài)的方法的例子,由于鎖定了方法所在對(duì)象對(duì)應(yīng)的Class對(duì)象,其他線程無(wú)法調(diào)用該方法所在對(duì)象其他的靜態(tài)synchronized方法:

/**
 * 定義一個(gè)類,包含了線程類需要調(diào)用的方法
 */
class Compute1{
 //這時(shí)如果某個(gè)線程調(diào)用該方法,
 //將鎖定synchronized方法所在對(duì)象對(duì)應(yīng)的class對(duì)象,
 //而不是鎖定synchronized方法所在對(duì)象
 public synchronized static void execute(){
  for(int i = 0; i<100; i++){
   try {
    Thread.sleep(100);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   System.out.println("compute1:execute1 " + i++);
  }
 }
 public synchronized static void execute2(){
  for(int i = 0; i<100; i++){
   try {
    Thread.sleep(100);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   System.out.println("compute1:execute2 " + i++);
  }
 }
}

main方法中兩個(gè)線程分別調(diào)用同一個(gè)對(duì)象的兩個(gè)static synchronized方法:

public static void main(String[] args) {
 Compute1 com = new Compute1();
 Thread thread1 = new Thread1(com);
 Thread thread2 = new Thread2(com);
 thread1.start();
 thread2.start();
}

一次只能調(diào)用一個(gè)靜態(tài)方法,直到執(zhí)行完成。

2.2、使用synchronized創(chuàng)建同步代碼塊:
通過(guò)使用synchronized同步代碼塊,鎖定一個(gè)對(duì)象,該對(duì)象作為可執(zhí)行的標(biāo)志從而達(dá)到同步的效果:

/**
 * 定義一個(gè)類,包含了線程類需要調(diào)用的方法
 */
class Compute1{
 //通過(guò)同步代碼塊鎖定object1對(duì)象進(jìn)行鎖定了其他同樣的synchronized代碼塊
 private Object object1 = new Object();
 public void execute(){
  synchronized(object1){
   for(int i = 0; i<100; i++){
    try {
     Thread.sleep(100);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    System.out.println("compute1:execute1 " + i++);
   }
  }

 }
 public synchronized void execute2(){
  synchronized(object1){
   for(int i = 0; i<100; i++){
    try {
     Thread.sleep(100);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    System.out.println("compute1:execute2 " + i++);
   }
  }
 }
}

如果想要使用synchronized同步代碼塊達(dá)到和使用synchronized方法同樣的效果,可以鎖定this引用:

synchronized(this){
 …
}

2.3、synchronized方法和synchronized同步代碼塊的區(qū)別:
synchronized同步代碼塊只是鎖定了該代碼塊,代碼塊外面的代碼還是可以被訪問(wèn)的。

synchronized方法是粗粒度的并發(fā)控制,某一個(gè)時(shí)刻只能有一個(gè)線程執(zhí)行該synchronized方法。

synchronized同步代碼塊是細(xì)粒度的并發(fā)控制,只會(huì)將塊中的代碼同步,代碼塊之外的代碼可以被其他線程同時(shí)訪問(wèn)。

相關(guān)文章

  • btrace定位生產(chǎn)故障的方法示例

    btrace定位生產(chǎn)故障的方法示例

    這篇文章主要介紹了btrace定位生產(chǎn)故障的方法示例,文中通過(guò)示例代碼介紹的很詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-02-02
  • Java實(shí)現(xiàn)五子棋游戲單機(jī)版(1.0)

    Java實(shí)現(xiàn)五子棋游戲單機(jī)版(1.0)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)五子棋游戲單機(jī)版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Java面試題沖刺第十三天--數(shù)據(jù)庫(kù)(3)

    Java面試題沖刺第十三天--數(shù)據(jù)庫(kù)(3)

    這篇文章主要為大家分享了最有價(jià)值的三道數(shù)據(jù)庫(kù)面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下
    2021-07-07
  • SpringBoot如何添加WebSocket的方法示例

    SpringBoot如何添加WebSocket的方法示例

    這篇文章主要介紹了SpringBoot如何添加WebSocket的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • 一文帶你了解Java中的ForkJoin

    一文帶你了解Java中的ForkJoin

    這篇文章主要介紹了一文帶你了解Java中的ForkJoin,F(xiàn)orkJoinTask本身的依賴關(guān)系并不復(fù)雜,它與異步任務(wù)計(jì)算FutureTask一樣均實(shí)現(xiàn)了Future接口,下文更多相關(guān)資料,需要的小伙伴可以參考一下
    2022-04-04
  • java中Servlet處理亂碼的方法

    java中Servlet處理亂碼的方法

    java中Servlet處理亂碼的方法,需要的朋友可以參考一下
    2013-03-03
  • java的新特性反射機(jī)制應(yīng)用及操作示例詳解

    java的新特性反射機(jī)制應(yīng)用及操作示例詳解

    這篇文章主要為大家介紹了java的新特性反射機(jī)制的操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • JAVA JVM面試題總結(jié)

    JAVA JVM面試題總結(jié)

    JVM 可以屏蔽與具體操作系統(tǒng)平臺(tái)相關(guān)的信息,使 Java 程序只需生成在 Java 虛擬機(jī)上運(yùn)行的目標(biāo)代碼,就可以在不同的平臺(tái)上運(yùn)行。這篇文章主要介紹了JAVA JVM面試題總結(jié),大家可以參考一下
    2021-08-08
  • SpringBoot+WebMagic實(shí)現(xiàn)網(wǎng)頁(yè)爬蟲(chóng)的示例代碼

    SpringBoot+WebMagic實(shí)現(xiàn)網(wǎng)頁(yè)爬蟲(chóng)的示例代碼

    本文是對(duì)spring?boot+WebMagic+MyBatis做了整合,使用WebMagic爬取數(shù)據(jù),然后通過(guò)MyBatis持久化爬取的數(shù)據(jù)到mysql數(shù)據(jù)庫(kù),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • 淺談Java垃圾回收機(jī)制

    淺談Java垃圾回收機(jī)制

    Java 中,程序員不需要關(guān)心所有不再使用的對(duì)象。垃圾回收機(jī)制自動(dòng)銷毀這些對(duì)象。垃圾回收機(jī)制是守護(hù)線程的最佳示例,因?yàn)樗冀K在后臺(tái)運(yùn)行。垃圾回收機(jī)制的主要目標(biāo)是通過(guò)銷毀無(wú)法訪問(wèn)的對(duì)象來(lái)釋放堆內(nèi)存。下面我們就來(lái)詳細(xì)介紹吧
    2021-09-09

最新評(píng)論