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

首頁 > 編程 > Java > 正文

Java中synchronized和Lock實現并發鎖

2019-11-06 06:27:05
字體:
來源:轉載
供稿:網友

前言

參考文章: 1. java 多線程:synchronized 關鍵字用法(修飾類,方法,靜態方法,代碼塊) 2. Java 多線程:Lock 接口(接口方法分析,ReentrantLock,ReadWriteLock) 3. synchronized 與 Lock 的那點事 4. Java并發編程:Lock 5. ReentrantLock(重入鎖)以及公平性 參考書籍:《瘋狂Java講義(第二版)》。

synchronized

  創建一個賬戶類Account,有姓名和賬戶余額屬性以及getset方法:

public class Account { PRivate String name; private double money; public Account(String name, double money) { super(); this.name = name; this.money = money; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; }}

  線程類是實現Runnable接口的Bank類,代碼如下:

public class Bank implements Runnable { private Account account; private double getMoney; // 本次取錢數量 public Bank(Account account, double getMoney) { super(); this.account = account; this.getMoney = getMoney; } @Override public void run() { getMoney(); } private void getMoney() { if (account.getMoney() >= getMoney) { System.out.println("取錢成功!取出:" + getMoney + " 元!"); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } account.setMoney(account.getMoney() - getMoney); System.out.println(account.getName() + "賬戶余額為:" + account.getMoney() + " 元!"); } else { System.out.println("取錢失敗,余額不足!"); } }}

  main函數如下:

public class Test { public static void main(String[] args) { Account account = new Account("Rojer", 2000); Bank bank = new Bank(account, 1200); new Thread(bank).start(); new Thread(bank).start(); }}

  運行結果如下:

取錢成功!取出:1200.0 元!取錢成功!取出:1200.0 元!Rojer賬戶余額為:800.0 元!Rojer賬戶余額為:800.0 元!

  從結果中看出,兩個線程都取錢成功了,這樣銀行是會虧死的,現在要實現一個時間段內只能有一個線程執行取錢操作,可以用synchronized關鍵字來進行加鎖。

修飾方法

  注意,synchronized不能修飾接口中的方法,而且父類中的synchronized方法被子類繼承以后,是沒有繼承synchronized的,如果要使用,還需要加上synchronized關鍵字。   用synchronized關鍵字來修飾Bank類的getMoney方法:

private synchronized void getMoney() { if (account.getMoney() >= getMoney) { System.out.println("取錢成功!取出:" + getMoney + " 元!"); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } account.setMoney(account.getMoney() - getMoney); System.out.println(account.getName() + "賬戶余額為:" + account.getMoney() + " 元!"); } else { System.out.println("取錢失敗,余額不足!"); } }

  運行結果如下:

取錢成功!取出:1200.0 元!Rojer賬戶余額為:800.0 元!取錢失敗,余額不足!

  可以看出程序運行結果和我們期待的一樣,只有一個線程取錢成功,第二個線程取錢的時候失敗了,因為余額不足。   用synchronized修飾方法時,所有的synchronized修飾的方法都被鎖住了,只能供當前擁有鎖的線程使用,但是其他的非synchronized修飾的方法沒有被鎖住,是可以正常調用的。HashTable中就是這樣實現線程安全的,可以參考我的另一篇文章-深入JDK源碼之Hashtable。所以說,用synchronized來修飾方法是重量級的。

修飾代碼塊

  修飾代碼塊就是以下面的方式,用synchronized關鍵字“包裹”住要同步的代碼塊:

synchronized (...) { ...}

  括號中可以是一個對象,也可以是一個類的class。如果是一個對象的話,那么只對該對象的線程進行加鎖,如果是修飾了一個類的class,如上文的Bank.class,那么無論有多少個Bank對象,并發時只能有一個對象的一個線程訪問同步代碼塊,其他線程都要等待,但是訪問沒有synchronized鎖住的代碼塊不受影響。

鎖住class

  修改getMoney代碼如下所示:

private void getMoney() { synchronized (Bank.class) { if (account.getMoney() >= getMoney) { System.out.println("取錢成功!取出:" + getMoney + " 元!"); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } account.setMoney(account.getMoney() - getMoney); System.out.println(account.getName() + "賬戶余額為:" + account.getMoney() + " 元!"); } else { System.out.println("取錢失敗,余額不足!"); } } }

  執行程序結果如下:

取錢成功!取出:1200.0 元!Rojer賬戶余額為:800.0 元!取錢失敗,余額不足!

鎖住對象

  修改getMoney代碼,鎖住賬戶account對象,如下所示:

private void getMoney() { synchronized (account) { if (account.getMoney() >= getMoney) { System.out.println("取錢成功!取出:" + getMoney + " 元!"); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } account.setMoney(account.getMoney() - getMoney); System.out.println(account.getName() + "賬戶余額為:" + account.getMoney() + " 元!"); } else { System.out.println("取錢失敗,余額不足!"); } }}

  運行結果如下:

取錢成功!取出:1200.0 元!Rojer賬戶余額為:800.0 元!取錢失敗,余額不足!

  synchronized修飾的是私有的account對象,因此當前對象的其他線程如果要使用account對象,就必須先拿到對象的鎖才可以。   相比于synchronized修飾方法,修飾代碼段的同步粒度更小,更加輕量。

修飾靜態方法

  靜態方法是所有類的對象共享的,因此用synchronized修飾靜態方法,其實就是修飾類,即一個線程擁有該靜態方法的監視器的時候,其他所有該類的對象,或者直接調用該靜態方法的線程,都需要等待當前線程釋放監視器。

Lock

  從 Java 5 開始,Java提供了一種更為強大的線程同步機制——通過顯示定義同步鎖對象來實現同步,在這種機制下,同步鎖由Lock對象充當。   Lock提供了比synchronized方法和synchronized代碼塊更廣泛的鎖定操作,Lock允許實現更靈活的結構,可以具有差別很大的屬性,并且支持多個相關的Condition對象。

—瘋狂Java講義

Lock接口

  Lockjava.util.concurrent.locks包中的一個接口,源碼如下:

public interface Lock { // 用來獲取鎖,如果鎖已被其他線程獲取,則進行等待。 void lock(); // 如果獲取鎖成功返回true,否則返回false boolean tryLock(); // 拿不到鎖時會等待一定的時間,在時間期限之內如果還拿不到鎖,就返回false。 // 如果如果一開始拿到鎖或者在等待期間內拿到了鎖,則返回true boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 通過這個方法去獲取鎖時,如果線程正在等待獲取鎖,則這個線程能夠響應中斷,即中斷線程的等待狀態 void lockInterruptibly() throws InterruptedException; // 釋放鎖 void unlock(); Condition newCondition();}

  ReentrantLock(可重入鎖)是Lock接口的實現類,可重入,也就是可以在當前線程上對資源再加一個鎖,相應的有一個值在記錄加了幾重鎖,如果釋放鎖時沒有釋放完,則會報錯。同時,ReentrantLock有個特性就是公平性,這個后面再說。

ReadWriteLock接口

  還有一個接口ReadWriteLock,顧名思義是讀寫鎖:

public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing */ Lock writeLock();}

  ReentrantReadWriteLock實現了ReadWriteLock接口,如果調用readLock().lock()的話,被當前線程鎖住的資源,其他所有線程都可以進行讀,但是不能進行寫,也不允許其他線程進行writeLock().lock()來對當前資源進行加寫鎖;如果一個線程調用寫鎖writeLock().lock(),那么之后當前線程可以進行寫操作,其他線程不能同時寫。


  上面說到的公平性,如果是非公平的,就是JVM決定喚醒哪個等待線程獲得鎖,而公平的鎖,即喚醒當前在等待的線程中,等待時間最長的那個線程獲得鎖。默認情況下,ReentrantLockReentrantReadWriteLock都是非公平鎖,但是可以設置為公平鎖,相對于公平鎖,非公平鎖有更好的效率,因為公平的獲取鎖沒有考慮到操作系統對線程的調度因素,這樣造成JVM對于等待中的線程調度次序和操作系統對線程的調度之間的不匹配,而且對于鎖的快速且重復的獲取過程中,連續獲取的概率是非常高的,而公平鎖會壓制這種情況,雖然公平性得以保障,但是響應比卻下降了。

總結

  相比于synchronized的笨重,Lock更加靈活,如果獲取synchronized鎖的線程阻塞了,那么其他等待鎖的線程只能等待,十分影響程序效率,而Lock可以讓線程只等待一段時間或者中斷線程的等待,還可以加讀鎖,同時多個線程都可以進行讀,效率十分高。   但是Lock實現鎖需要程序員多操點心了,獲取synchronized鎖的線程執行完畢之后就會釋放鎖,不需要手動釋放,而且如果線程發生異常,JVM會讓線程自動釋放鎖,但是Lock鎖需要程序員手動釋放鎖,并且要處理異常。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 苍南县| 宜宾县| 永清县| 乐清市| 五大连池市| 滨海县| 三都| 惠水县| 林周县| 海晏县| 乐陵市| 沙田区| 恩施市| 渭源县| 祁门县| 陇南市| 灵武市| 平阳县| 潜山县| 修文县| 屏东县| 临潭县| 舒城县| 石城县| 乌兰县| 临沂市| 贵定县| 星子县| 永善县| 武鸣县| 沁阳市| 孝义市| 砀山县| 乌苏市| 五大连池市| 安图县| 兴义市| 巫山县| 江油市| 绥化市| 新泰市|