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

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

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

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

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

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

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

上代碼,具體展示“過度消耗/虛擬消耗”問題:

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 //過度消耗
Thread-1賣票:ticket = 1
Thread-2賣票:ticket = 0

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

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

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

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

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

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

①使用synchronized同步代碼塊;

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

下面分別進(jìn)行解析,對上面售票的代碼進(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同步代碼塊只是鎖定了該代碼塊,代碼塊外面的代碼還是可以被訪問的。

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

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

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

1.synchronized同步鎖的引入

/*
 * 非線程安全
 * */
//多個線程共同訪問一個對象中的實(shí)例變量,則會出現(xiàn)"非線程安全"問題
class MyRunnable1 implements Runnable{
 private int num = 10;
 public void run() {
 try {
  if(num > 0) {
  System.out.println(""+Thread.currentThread().getName()+"開始"+",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();
 }
}

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

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

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

2. 對象鎖

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

若我們對如上代碼進(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()+"開始"+",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é)論:若兩個線程同時訪問同一個對象中的同步方法時一定是線程安全的。

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

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

/*
 * 同步代碼塊
 * */
class MyRunnable1 implements Runnable{
 private int num = 10;
 public void run() {
 try {
  synchronized (this) {
  if(num > 0) {
   System.out.println(""+Thread.currentThread().getName()+"開始"+",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鎖多對象

/*
 * synchronized鎖多對象
 * */
class Sync{
 public synchronized void print() {
 System.out.println("print方法開始:"+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鎖多個對象時不能實(shí)現(xiàn)同步操作,由此可以得出關(guān)鍵字synchronized取得的鎖都是對象鎖,而不是將一段代碼或者方法(函數(shù))當(dāng)作鎖。哪個線程先執(zhí)行帶synchronized關(guān)鍵字的方法或synchronized代碼塊,哪個線程就有該方法或該代碼塊所持有的鎖,其他線程只能呈現(xiàn)等待狀態(tài),前提是多個線程訪問同一個對象。

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

3.全局鎖

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

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

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

/*
 * synchronized用在static方法上
 * */
class Sync{
 static public synchronized void print() {
 System.out.println("print方法開始:"+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對類的Class對象進(jìn)行上鎖

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

/*
 * synchronized對類的Class對象上鎖
 * */
class Sync{
 public void print() {
 synchronized (Sync.class) {
  System.out.println("print方法開始:"+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īng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。

相關(guān)文章

  • Springboot項(xiàng)目啟動時如何用命令動態(tài)指定環(huán)境

    Springboot項(xiàng)目啟動時如何用命令動態(tài)指定環(huán)境

    這篇文章主要介紹了Springboot項(xiàng)目啟動時如何用命令動態(tài)指定環(huán)境的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • java 中的static關(guān)鍵字和final關(guān)鍵字的不同之處

    java 中的static關(guān)鍵字和final關(guān)鍵字的不同之處

    java 中的static關(guān)鍵字和final關(guān)鍵字的不同之處,需要的朋友可以參考一下
    2013-03-03
  • java實(shí)現(xiàn)順時針打印矩陣

    java實(shí)現(xiàn)順時針打印矩陣

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)順時針打印矩陣的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • Spring Security如何使用URL地址進(jìn)行權(quán)限控制

    Spring Security如何使用URL地址進(jìn)行權(quán)限控制

    這篇文章主要介紹了Spring Security如何使用URL地址進(jìn)行權(quán)限控制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • MyBatis中#{}和${}的區(qū)別詳解

    MyBatis中#{}和${}的區(qū)別詳解

    mybatis和ibatis總體來講都差不多的。下面小編給大家探討下mybatis中#{}和${}的區(qū)別,感興趣的朋友一起學(xué)習(xí)吧
    2016-08-08
  • jdk安裝、Java環(huán)境配置方法詳解

    jdk安裝、Java環(huán)境配置方法詳解

    這篇文章主要介紹了jdk安裝、Java環(huán)境配置方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • 使用Java校驗(yàn)SQL語句的合法性五種解決方案

    使用Java校驗(yàn)SQL語句的合法性五種解決方案

    這篇文章主要介紹了如何用java校驗(yàn)SQL語句的合法性(提供五種解決方案),使用JDBC?API和JSqlParser庫、正則表達(dá)式、ANTLR解析器生成器或Apache?Calcite庫都可以實(shí)現(xiàn)校驗(yàn)SQL語句的合法性,需要的朋友可以參考下
    2023-04-04
  • SpringMVC的執(zhí)行過程淺析

    SpringMVC的執(zhí)行過程淺析

    這篇文章主要給大家介紹了關(guān)于SpringMVC的執(zhí)行過程的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用SpringMVC具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 詳解如何判斷Java線程池任務(wù)已執(zhí)行完

    詳解如何判斷Java線程池任務(wù)已執(zhí)行完

    線程池的使用并不復(fù)雜,麻煩的是如何判斷線程池中的任務(wù)已經(jīng)全部執(zhí)行完了,所以接下來,我們就來看看如何判斷線程中的任務(wù)是否已經(jīng)全部執(zhí)行完吧
    2023-08-08
  • Java集合List與Array的相互轉(zhuǎn)換

    Java集合List與Array的相互轉(zhuǎn)換

    本篇文章主要介紹了Java集合List與Array的相互轉(zhuǎn)換,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02

最新評論