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

利用synchronized實(shí)現(xiàn)線程同步的案例講解

 更新時(shí)間:2021年02月20日 10:05:36   作者:Chin_style  
這篇文章主要介紹了利用synchronized實(shí)現(xiàn)線程同步的案例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

一、前期基礎(chǔ)知識(shí)儲(chǔ)備

(1)線程同步的定義:多線程之間的同步。

(2)多線程同步原因:一個(gè)多線程的程序如果是通過(guò)Runnable接口實(shí)現(xiàn)的,則意味著類中的屬性將被多個(gè)線程共享,由此引出資源的同步問(wèn)題,即當(dāng)多個(gè)線程要操作同一資源時(shí),有可能出現(xiàn)錯(cuò)誤。

(3)實(shí)現(xiàn)多線程同步的方式——引入同步機(jī)制:在線程使用一個(gè)資源時(shí)為其加鎖,這樣其他的線程便不能訪問(wèn)那個(gè)資源了,直到解鎖后才可以訪問(wèn)?!@樣做的結(jié)果,所有線程間會(huì)有資源競(jìng)爭(zhēng),但是所有競(jìng)爭(zhēng)的資源是同步的,刷新的,動(dòng)態(tài)的,不會(huì)因?yàn)榫€程間的競(jìng)爭(zhēng),導(dǎo)致資源“過(guò)度消耗”或者“虛擬消耗”。

上代碼,具體展示“過(guò)度消耗/虛擬消耗”問(wèn)題:

public class TestTicketRunnable{
  public static void main(String[] a){
    TicketThread tThread = new TicketThread();
    new Thread(tThread).start();
    new Thread(tThread).start();
    new Thread(tThread).start();
  }
};
class TicketThread implements Runnable {
  private int ticket = 5;
  public void run(){
    for (int i = 0; i < 5; i++){
      if (ticket > 0){
        try {
          Thread.sleep(300);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "賣票:ticket = " + ticket--);
      }
    }
  }
};

運(yùn)行結(jié)果:

Thread-0賣票:ticket = 5
Thread-2賣票:ticket = 5 //虛擬消耗
Thread-1賣票:ticket = 4
Thread-1賣票:ticket = 2
Thread-2賣票:ticket = 3
Thread-0賣票:ticket = 3 //虛擬消耗
Thread-0賣票:ticket = -1 //過(guò)度消耗
Thread-1賣票:ticket = 1
Thread-2賣票:ticket = 0

如上所見(jiàn),票一共5張,三個(gè)線程調(diào)用買票,線程1網(wǎng)上賣了售票第1張,緊接著線程2線下也賣了“第一張”出現(xiàn)了“虛擬消耗”的問(wèn)題;線程3黃牛黨賣了最后1張票,線程1網(wǎng)上又賣了最后1張,出現(xiàn)了“過(guò)度消耗”的問(wèn)題,這兩種問(wèn)題都是實(shí)際生活中不可能發(fā)生的,但是在這個(gè)3個(gè)線程執(zhí)行中卻出現(xiàn)了,那一定是有問(wèn)題的,問(wèn)題的根源就在于,三個(gè)渠道的“售票員”不在一個(gè)頻道上辦事,或者說(shuō)沒(méi)有相互之間同步所共享的資源,導(dǎo)致這一問(wèn)題的根本原因,就是相互之間實(shí)現(xiàn)方式不同步。

二、使用synchronized實(shí)現(xiàn)同步機(jī)制

synchronized關(guān)鍵字:Java語(yǔ)言的關(guān)鍵字,可用來(lái)給對(duì)象和方法或者代碼塊加鎖,當(dāng)它鎖定一個(gè)方法或者一個(gè)代碼塊的時(shí)候,同一時(shí)刻最多只有一個(gè)線程執(zhí)行這段代碼。

當(dāng)兩個(gè)并發(fā)線程訪問(wèn)同一個(gè)對(duì)象object中的這個(gè)加鎖同步代碼塊時(shí),一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊。

它包括兩種用法:synchronized 方法和 synchronized 塊。

即實(shí)現(xiàn)線程間同步的方式有兩種:

①使用synchronized同步代碼塊;

②使用synchronized關(guān)鍵字創(chuàng)建synchronized()方法

下面分別進(jìn)行解析,對(duì)上面售票的代碼進(jìn)行改造:

①代碼——使用synchronized同步代碼塊

class TicketThread implements Runnable {
  private int ticket = 5;
  public void run(){
    for (int i = 0; i < 5; i++){
      synchronized(this){
        if (ticket > 0){
        try {
          Thread.sleep(300);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "賣票:ticket = " + ticket--);
      }
      }
    }
  }
}

②代碼——使用synchronized關(guān)鍵字創(chuàng)建synchronized()方法

class TicketThreadMethod implements Runnable {
  private int ticket = 5;
  public void run(){
    for (int i = 0; i < 5; i++){
      this.sale();
    }
  }
  public synchronized void sale(){
      if (ticket > 0){
        try {
          Thread.sleep(300);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "賣票:ticket = " + ticket--);
      }
  }
}

三、synchronized方法和synchronized同步代碼塊的區(qū)別:

synchronized同步代碼塊只是鎖定了該代碼塊,代碼塊外面的代碼還是可以被訪問(wèn)的。

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

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

補(bǔ)充:多線程同步鎖synchronized(對(duì)象鎖與全局鎖)總結(jié)

1.synchronized同步鎖的引入

/*
 * 非線程安全
 * */
//多個(gè)線程共同訪問(wèn)一個(gè)對(duì)象中的實(shí)例變量,則會(huì)出現(xiàn)"非線程安全"問(wèn)題
class MyRunnable1 implements Runnable{
 private int num = 10;
 public void run() {
 try {
  if(num > 0) {
  System.out.println(""+Thread.currentThread().getName()+"開(kāi)始"+",num= "+num--);
  Thread.sleep(1000);
  System.out.println(""+Thread.currentThread().getName()+"結(jié)束");
  }
 } catch (InterruptedException e) {
  e.printStackTrace();
 }
 }
}public class Test5_5{
 public static void main(String[] args) {
 MyRunnable1 myRunnable1 = new MyRunnable1();
 Thread thread1 = new Thread(myRunnable1,"線程1");
 Thread thread2 = new Thread(myRunnable1,"線程2");
 thread1.start();
 thread2.start();
 }
}

上例說(shuō)明兩個(gè)線程同時(shí)訪問(wèn)一個(gè)沒(méi)有同步的方法,如果兩個(gè)線程同時(shí)操作業(yè)務(wù)對(duì)象中的實(shí)例變量,則會(huì)出現(xiàn)“線程不安全”問(wèn)題。

由此我們引入synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)同步問(wèn)題:

在Java中使用synchronized關(guān)鍵字控制線程同步,控制synchronized代碼段不被多個(gè)線程同時(shí)執(zhí)行,synchronized即可以使用在方法上也可以使用在代碼塊中。

2. 對(duì)象鎖

(1)synchronized方法(對(duì)當(dāng)前對(duì)象進(jìn)行加鎖)

若我們對(duì)如上代碼進(jìn)行修改,在run()方法上加入synchronized關(guān)鍵字使其變?yōu)橥椒椒ā?/p>

/*
 * 同步方法
 * */
class MyRunnable1 implements Runnable{
 private int num = 10;
 public void run() {
 this.print();
 }
 
 public synchronized void print() {
 if(this.num > 0) {
  System.out.println(""+Thread.currentThread().getName()+"開(kāi)始"+",num= "+num--);
  try {
  Thread.sleep(1000);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  System.out.println(""+Thread.currentThread().getName()+"結(jié)束");
 }
 }
}public class Test5_5{
 public static void main(String[] args) {
 MyRunnable1 myRunnable1 = new MyRunnable1();
 Thread thread1 = new Thread(myRunnable1,"線程1");
 Thread thread2 = new Thread(myRunnable1,"線程2");
 thread1.start();
 thread2.start();
 }
}  

結(jié)論:若兩個(gè)線程同時(shí)訪問(wèn)同一個(gè)對(duì)象中的同步方法時(shí)一定是線程安全的。

(2)synchronized代碼塊(對(duì)某一個(gè)對(duì)象進(jìn)行加鎖)

如果要使用同步代碼塊必須設(shè)置一個(gè)要鎖定的對(duì)象,所以一般可以鎖定當(dāng)前對(duì)象:this.

/*
 * 同步代碼塊
 * */
class MyRunnable1 implements Runnable{
 private int num = 10;
 public void run() {
 try {
  synchronized (this) {
  if(num > 0) {
   System.out.println(""+Thread.currentThread().getName()+"開(kāi)始"+",num= "+num--);
   Thread.sleep(1000);
   System.out.println(""+Thread.currentThread().getName()+"結(jié)束");
  } 
  }
 } catch (InterruptedException e) {
  e.printStackTrace();
 }
 }
}
 
public class Test5_5{
 public static void main(String[] args) {
 MyRunnable1 myRunnable1 = new MyRunnable1();
 Thread thread1 = new Thread(myRunnable1,"線程1");
 Thread thread2 = new Thread(myRunnable1,"線程2");
 thread1.start();
 thread2.start();
 }
} 

(3)synchronized鎖多對(duì)象

/*
 * synchronized鎖多對(duì)象
 * */
class Sync{
 public synchronized void print() {
 System.out.println("print方法開(kāi)始:"+Thread.currentThread().getName());
 try {
  Thread.sleep(1000);
 } catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
 System.out.println("print方法結(jié)束"+Thread.currentThread().getName());
 }
}
class MyThread extends Thread{
 public void run() {
 Sync sync = new Sync();
 sync.print();
 }
}
public class Test5_5{
 public static void main(String[] args) {
 for(int i = 0; i < 3;i++) {
  Thread thread = new MyThread();
  thread.start();
 }
 }
}

根據(jù)上例我們可以發(fā)現(xiàn)當(dāng)synchronized鎖多個(gè)對(duì)象時(shí)不能實(shí)現(xiàn)同步操作,由此可以得出關(guān)鍵字synchronized取得的鎖都是對(duì)象鎖,而不是將一段代碼或者方法(函數(shù))當(dāng)作鎖。哪個(gè)線程先執(zhí)行帶synchronized關(guān)鍵字的方法或synchronized代碼塊,哪個(gè)線程就有該方法或該代碼塊所持有的鎖,其他線程只能呈現(xiàn)等待狀態(tài),前提是多個(gè)線程訪問(wèn)同一個(gè)對(duì)象。

只有共享資源的讀寫(xiě)需要同步化,如果不是共享資源,那么就不需要同步化操作。

3.全局鎖

實(shí)現(xiàn)全局鎖有兩種方式:

(1) 將synchronized關(guān)鍵字用在static方法上

synchronized加到static靜態(tài)方法上是對(duì)Class類上鎖,而synchronized加到非static方法上是給對(duì)對(duì)象上鎖。Class鎖可以對(duì)類的所有對(duì)象實(shí)例起作用。

/*
 * synchronized用在static方法上
 * */
class Sync{
 static public synchronized void print() {
 System.out.println("print方法開(kāi)始:"+Thread.currentThread().getName());
 try {
  Thread.sleep(1000);
 } catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
 System.out.println("print方法結(jié)束"+Thread.currentThread().getName());
 }
}
class MyThread extends Thread{
 public void run() {
 Sync.print();
 }
}
public class Test5_5{
 public static void main(String[] args) {
 for(int i = 0; i < 3;i++) {
  Thread thread = new MyThread();
  thread.start();
 }
 }
}

(2) 用synchronized對(duì)類的Class對(duì)象進(jìn)行上鎖

synchronized(class)代碼塊的作用與synchronized static方法的作用一樣。

/*
 * synchronized對(duì)類的Class對(duì)象上鎖
 * */
class Sync{
 public void print() {
 synchronized (Sync.class) {
  System.out.println("print方法開(kāi)始:"+Thread.currentThread().getName());
  try {
  Thread.sleep(1000);
  } catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
  }
  System.out.println("print方法結(jié)束"+Thread.currentThread().getName());
 }
 }
}
class MyThread extends Thread{
 public void run() {
 Sync sync = new Sync();
 sync.print();
 }
}
public class Test5_5{
 public static void main(String[] args) {
 for(int i = 0; i < 3;i++) {
  Thread thread = new MyThread();
  thread.start();
 }
 }
}

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

相關(guān)文章

  • Netty搭建WebSocket服務(wù)器實(shí)戰(zhàn)教程

    Netty搭建WebSocket服務(wù)器實(shí)戰(zhàn)教程

    這篇文章主要介紹了Netty搭建WebSocket服務(wù)器實(shí)戰(zhàn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-03-03
  • 簡(jiǎn)述IDEA集成Git在實(shí)際項(xiàng)目中的運(yùn)用

    簡(jiǎn)述IDEA集成Git在實(shí)際項(xiàng)目中的運(yùn)用

    這篇文章主要介紹了IDEA集成Git在實(shí)際項(xiàng)目中的運(yùn)用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-07-07
  • mybatis-puls中的resultMap數(shù)據(jù)映射

    mybatis-puls中的resultMap數(shù)據(jù)映射

    這篇文章主要介紹了mybatis-puls中的resultMap數(shù)據(jù)映射,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java的可變參數(shù)與Collections類的功能示例解析

    Java的可變參數(shù)與Collections類的功能示例解析

    這篇文章主要為大家介紹了Java的可變參數(shù)與Collections類的功能示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • springcloud client指定注冊(cè)到eureka的ip與端口號(hào)方式

    springcloud client指定注冊(cè)到eureka的ip與端口號(hào)方式

    這篇文章主要介紹了springcloud client指定注冊(cè)到eureka的ip與端口號(hào)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java實(shí)現(xiàn)文件上傳與文件下載的示例代碼

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

    在開(kāi)發(fā)中項(xiàng)目難免會(huì)遇到文件上傳和下載的情況,這篇文章主要為大家詳細(xì)介紹了Java中實(shí)現(xiàn)文件上傳與文件下載的示例代碼,希望對(duì)大家有所幫助
    2023-07-07
  • mybatis集成到spring的方式詳解

    mybatis集成到spring的方式詳解

    這篇文章主要介紹了mybatis是如何集成到spring的,將mybatis集成到spring之后,就可以被spring的ioc容器托管,再也不用自己創(chuàng)建SqlSessionFactory?、打開(kāi)SqlSession等操作,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • Springboot?中的?Filter?實(shí)現(xiàn)超大響應(yīng)?JSON?數(shù)據(jù)壓縮的方法

    Springboot?中的?Filter?實(shí)現(xiàn)超大響應(yīng)?JSON?數(shù)據(jù)壓縮的方法

    這篇文章主要介紹了Springboot?中的?Filter?實(shí)現(xiàn)超大響應(yīng)?JSON?數(shù)據(jù)壓縮,定義GzipFilter對(duì)輸出進(jìn)行攔截,定義 Controller該 Controller 非常簡(jiǎn)單,主要讀取一個(gè)大文本文件,作為輸出的內(nèi)容,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-10-10
  • 支持SpEL表達(dá)式的自定義日志注解@SysLog介紹

    支持SpEL表達(dá)式的自定義日志注解@SysLog介紹

    這篇文章主要介紹了支持SpEL表達(dá)式的自定義日志注解@SysLog,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java中ExecutorService和ThreadPoolExecutor運(yùn)行原理

    Java中ExecutorService和ThreadPoolExecutor運(yùn)行原理

    本文主要介紹了Java中ExecutorService和ThreadPoolExecutor運(yùn)行原理,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08

最新評(píng)論

伊吾县| 田东县| 汝南县| 皮山县| 广宗县| 鹤山市| 公主岭市| 长垣县| 新营市| 家居| 华容县| 东乡族自治县| 铁力市| 梁河县| 临安市| 沾益县| 河间市| 团风县| 宁阳县| 延安市| 萝北县| 邛崃市| 孝义市| 海原县| 彰武县| 滕州市| 黑水县| 长白| 怀宁县| 鱼台县| 贵德县| 鞍山市| 吉林市| 开封县| 土默特左旗| 舞钢市| 滦平县| 开封县| 晋宁县| 永安市| 汉源县|