国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Java使用synchronized修飾方法來同步線程的實例演示

2019-11-26 14:11:06
字體:
來源:轉載
供稿:網友

Java中可以使用關鍵字synchronized進行線程同步控制,實現關鍵資源順序訪問,避免由于多線程并發執行導致的數據不一致性等問題。synchronized的原理是對象監視器(鎖),只有獲取到監視器的線程才能繼續執行,否則線程會等待獲取監視器。Java中每個對象或者類都有一把鎖與之相關聯,對于對象來說,監視的是這個對象的實例變量,對于類來說,監視的是類變量(一個類本身是類Class的對象,所以與類關聯的鎖也是對象鎖)。synchronized關鍵字使用方式有兩種:synchronized方法和synchronized塊。這兩種監視區域都和一個引入對象相關聯,當到達這個監視區域時,JVM就會鎖住這個引用對象,當離開時會釋放這個引用對象上的鎖(有異常退出時,JVM會釋放鎖)。對象鎖是JVM內部機制,只需要編寫同步方法或者同步塊即可,操作監視區域時JVM會自動獲取鎖或者釋放鎖。

示例1

先來看第一個示例,在java中,同一個對象的臨界區,在同一時間只有一個允許被訪問(都是非靜態的synchronized方法):

package concurrency;public class Main8 {  public static void main(String[] args) {    Account account = new Account();    account.setBalance(1000);    Company company = new Company(account);    Thread companyThread = new Thread(company);    Bank bank = new Bank(account);    Thread bankThread = new Thread(bank);    System.out.printf("Account : Initial Balance: %f/n", account.getBalance());    companyThread.start();    bankThread.start();    try {      //join()方法等待這兩個線程運行完成      companyThread.join();      bankThread.join();      System.out.printf("Account : Final Balance: %f/n", account.getBalance());    } catch (InterruptedException e) {      e.printStackTrace();    }  }}
/*帳戶*/class Account{  private double balance;  /*將傳入的數據加到余額balance中*/  public synchronized void addAmount(double amount){    double tmp = balance;    try {      Thread.sleep(10);    } catch (InterruptedException e) {      e.printStackTrace();    }    tmp += amount;    balance = tmp;  }  /*將傳入的數據從余額balance中扣除*/  public synchronized void subtractAmount(double amount){    double tmp = balance;    try {      Thread.sleep(10);    } catch (InterruptedException e) {      e.printStackTrace();    }    tmp -= amount;    balance = tmp;  }  public double getBalance() {    return balance;  }  public void setBalance(double balance) {    this.balance = balance;  }}
/*銀行*/class Bank implements Runnable{  private Account account;  public Bank(Account account){    this.account = account;  }  @Override  public void run() {    for (int i = 0; i < 100; i++) {      account.subtractAmount(1000);    }  }}
/*公司*/class Company implements Runnable{  private Account account;  public Company(Account account){    this.account = account;  }  @Override  public void run() {    for (int i = 0; i < 100; i++) {      account.addAmount(1000);    }  }}

你已經開發了一個銀行賬戶的模擬應用,它能夠對余額進行充值和扣除。這個程序通過調用100次addAmount()方法對帳戶進行充值,每次存入1000;然后通過調用100次subtractAmount()方法對帳戶余額進行扣除,每次扣除1000;我們期望帳戶的最終余額與起初余額相等,我們通過synchronized關鍵字實現了。

如果想查看共享數據的并發訪問問題,只需要將addAmount()和subtractAmount()方法聲明中的synchronized關鍵字刪除即可。在沒有synchronized關鍵字的情況下,打印出來的余額值并不一致。如果多次運行這個程序,你將獲取不同的結果。因為JVM并不保證線程的執行順序,所以每次運行的時候,線程將以不同的順序讀取并且修改帳戶的余額,造成最終結果也是不一樣的。

一個對象的方法采用synchronized關鍵字進行聲明,只能被一個線程訪問。如果線程A正在執行一個同步方法syncMethodA(),線程B要執行這個對象的其他同步方法syncMethodB(),線程B將被阻塞直到線程A訪問完。但如果線程B訪問的是同一個類的不同對象,那么兩個線程都不會被阻塞。

示例2

演示同一個對象上的靜態synchronized方法與非靜態synchronized方法可以在同一時間點被多個線程訪問的問題,驗證一下。

package concurrency;public class Main8 {  public static void main(String[] args) {    Account account = new Account();    account.setBalance(1000);    Company company = new Company(account);    Thread companyThread = new Thread(company);    Bank bank = new Bank(account);    Thread bankThread = new Thread(bank);    System.out.printf("Account : Initial Balance: %f/n", account.getBalance());    companyThread.start();    bankThread.start();    try {      //join()方法等待這兩個線程運行完成      companyThread.join();      bankThread.join();      System.out.printf("Account : Final Balance: %f/n", account.getBalance());    } catch (InterruptedException e) {      e.printStackTrace();    }  }}
/*帳戶*/class Account{  /*這里改為靜態變量*/  private static double balance = 0;  /*將傳入的數據加到余額balance中,注意是用static修飾過的*/  public static synchronized void addAmount(double amount){    double tmp = balance;    try {      Thread.sleep(10);    } catch (InterruptedException e) {      e.printStackTrace();    }    tmp += amount;    balance = tmp;  }  /*將傳入的數據從余額balance中扣除*/  public synchronized void subtractAmount(double amount){    double tmp = balance;    try {      Thread.sleep(10);    } catch (InterruptedException e) {      e.printStackTrace();    }    tmp -= amount;    balance = tmp;  }  public double getBalance() {    return balance;  }  public void setBalance(double balance) {    this.balance = balance;  }}
/*銀行*/class Bank implements Runnable{  private Account account;  public Bank(Account account){    this.account = account;  }  @Override  public void run() {    for (int i = 0; i < 100; i++) {      account.subtractAmount(1000);    }  }}
/*公司*/class Company implements Runnable{  private Account account;  public Company(Account account){    this.account = account;  }  @Override  public void run() {    for (int i = 0; i < 100; i++) {      account.addAmount(1000);    }  }}

我只是把上個例子中的,balance加了static關鍵字修改,addAmount()方法也可以static關鍵字修飾。執行結果大家可以自己測試一下,每次執行都是不一樣的結果!

一些總結:

  • synchronized是通過軟件(JVM)實現的,簡單易用,即使在JDK5之后有了Lock,仍然被廣泛地使用。
  • synchronized實際上是非公平的,新來的線程有可能立即獲得監視器,而在等待區中等候已久的線程可能再次等待,不過這種搶占的方式可以預防饑餓。
  • synchronized只有鎖只與一個條件(是否獲取鎖)相關聯,不靈活,后來Condition與Lock的結合解決了這個問題。
  • 多線程競爭一個鎖時,其余未得到鎖的線程只能不停的嘗試獲得鎖,而不能中斷。高并發的情況下會導致性能下降。ReentrantLock的lockInterruptibly()方法可以優先考慮響應中斷。 一個線程等待時間過長,它可以中斷自己,然后ReentrantLock響應這個中斷,不再讓這個線程繼續等待。有了這個機制,使用ReentrantLock時就不會像synchronized那樣產生死鎖了。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 临邑县| 佳木斯市| 容城县| 专栏| 澳门| 广汉市| 内丘县| 华阴市| 太仆寺旗| 甘肃省| 修武县| 金川县| 汕尾市| 潮安县| 姚安县| 襄樊市| 常州市| 措美县| 大竹县| 喜德县| 栖霞市| 临夏县| 安泽县| 黄梅县| 贵南县| 上杭县| 玉环县| 青铜峡市| 阿拉善盟| 翼城县| 闻喜县| 澳门| 彭泽县| 保亭| 苏州市| 剑川县| 井陉县| 日照市| 肇源县| 夏邑县| 赞皇县|