12|如何用面向?qū)ο笏枷雽懞貌l(fā)程序

在工作中,我發(fā)現(xiàn)很多同學(xué)在設(shè)計之初都是直接按照單線程的思路來寫程序的。

而忽略了本應(yīng)該重視的并發(fā)問題。

關(guān)于這個問題,我們今天來聊聊 如何用面向?qū)ο笏枷雽懞貌l(fā)程序。

面向?qū)ο笏枷敫l(fā)編程有關(guān)系嘛?本來是沒有關(guān)系的,他們分屬兩個不同的領(lǐng)域,但是在Java 語言里,這兩個領(lǐng)域被無情的融合在一起了。**在 Java 語言里,面向?qū)ο笏枷肽軌蜃尣l(fā)編程變得更簡單。 **

那么如何才能用面向?qū)ο笏枷雽懞貌l(fā)程序呢?結(jié)合我自己的經(jīng)驗來看,我覺得可以從封裝共享變量,識別共享變量間的約束條件和指定并發(fā)訪問策略這三個方面下手。

一、封裝共享變量

并發(fā)程序,我們關(guān)注的一個核心問題,不過是解決多線程同時訪問共享變量的問題。編程領(lǐng)域?qū)τ诠蚕碜兞康脑L問,必須嚴(yán)格控制,好在有了面向?qū)ο笏枷?,對于變量的訪問路徑可以輕松把控。

面型對象里面有一個很重要的特性就是封裝,封裝的通俗解釋就是將屬性和細(xì)節(jié)封裝在對象內(nèi)部。外界對象只能通過目標(biāo)對象提供的公共方法來間接訪問這些內(nèi)部屬性,我們把共享變量作為對象的屬性,那對于共享變量的訪問路徑就是對象的公共方法。

利用面向?qū)ο笏枷雽懖l(fā)程序,其實就這么簡單,**將共享變量作為對象屬性封裝在內(nèi)部,對所有公共方法制定并發(fā)訪問策略。 **

就拿很多都會用到的計數(shù)器來說,下面的計數(shù)器程序共享變量只有一個,就是value ,我們把他作為Counter 類的屬性,并且將兩個公共方法 get() 和 addOne() 聲明為同步方法,這樣 Counter 類就成為一個線程安全的類了。


public class Counter {
  private long value;
  synchronized long get(){
    return value;
  }
  synchronized long addOne(){
    return ++value;
  }
}

當(dāng)然很多實際工作中要復(fù)雜的多,對于那些不會發(fā)生變化的共享變量來說,建議你用fianl 關(guān)鍵字修飾。這樣既可以避免并發(fā)問題,也能很明了的表明你的設(shè)計意圖,讓后面接手你的兄弟知道,你已經(jīng)考慮這些共享變量的安全性問題了。

二、識別共享變量間的約束條件

識別共享變量的約束條件非常重要,因為這些約束條件,決定了并發(fā)訪問策略,例如:庫存管理里面有個合理庫存的概念,庫存量不能太高,也不能太低,他有一個上限和一個下限,我們可以通過下面代碼模擬以下:

在類 SafeWM 中,聲明了兩個成員變量 upper 和 lower,分別代表庫存上限和庫存下限,這兩個變量用了 AtomicLong 這個原子類,原子類是線程安全的,所以這兩個成員變量的 set 方法就不需要同步了。


public class SafeWM {
  // 庫存上限
  private final AtomicLong upper =
        new AtomicLong(0);
  // 庫存下限
  private final AtomicLong lower =
        new AtomicLong(0);
  // 設(shè)置庫存上限
  void setUpper(long v){
    upper.set(v);
  }
  // 設(shè)置庫存下限
  void setLower(long v){
    lower.set(v);
  }
  // 省略其他業(yè)務(wù)代碼
}

在沒有識別出庫存下限要小于庫存上限這個約束條件之前,我們制定的并發(fā)訪問策略是利用原子類,但是這個策略,完全不能保證庫存下限要小于庫存上限這個約束條件。所以說,在設(shè)計階段,我們一定要識別出所有共享變量之間的約束條件,如果約束條件識別不足,很可能導(dǎo)致制定的并發(fā)訪問策略南轅北轍。共享變量之間的約束條件,反映在代碼里,基本上都會有 if 語句,所以,一定要特別注意競態(tài)條件。

三、制定并發(fā)訪問策略

制定并發(fā)訪問策略,是一個非常復(fù)雜的事情。應(yīng)該說整個專欄都是在嘗試搞定它。不過從方案上來看,無外乎就是以下“三件事”。

1、避免共享:避免共享的技術(shù)主要是利于線程本地存儲以及為每個任務(wù)分配獨立的線程。

2、不變模式:這個在 Java 領(lǐng)域應(yīng)用的很少,但在其他領(lǐng)域卻有著廣泛的應(yīng)用,例如 Actor 模式、CSP 模式以及函數(shù)式編程的基礎(chǔ)都是不變模式。

3、管程及其他同步工具:Java 領(lǐng)域萬能的解決方案是管程,但是對于很多特定場景,使用 Java 并發(fā)包提供的讀寫鎖、并發(fā)容器等同步工具會更好。

除了這些方案之外,還有一些宏觀的原則需要你了解。這些宏觀原則,有助于你寫出“健壯”的并發(fā)程序。這些原則主要有以下三條。

1、優(yōu)先使用成熟的工具類:Java SDK 并發(fā)包里提供了豐富的工具類,基本上能滿足你日常的需要,建議你熟悉它們,用好它們,而不是自己再“發(fā)明輪子”,畢竟并發(fā)工具類不是隨隨便便就能發(fā)明成功的。

2、迫不得已時才使用低級的同步原語:低級的同步原語主要指的是 synchronized、Lock、Semaphore 等,這些雖然感覺簡單,但實際上并沒那么簡單,一定要小心使用。

3、避免過早優(yōu)化:安全第一,并發(fā)程序首先要保證安全,出現(xiàn)性能瓶頸后再優(yōu)化。在設(shè)計期和開發(fā)期,很多人經(jīng)常會情不自禁地預(yù)估性能的瓶頸,并對此實施優(yōu)化,但殘酷的現(xiàn)實卻是:性能瓶頸不是你想預(yù)估就能預(yù)估的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 他們是天之驕子。有著青春陽光的面孔,天使般的性格,高貴的氣質(zhì),精深的專業(yè)知識,謙虛好學(xué)的態(tài)度,堪稱遵紀(jì)守法的良好公...
    懶懶的阿朱閱讀 315評論 0 0
  • 就在剛剛被孩子氣的火冒三丈,快九點時,打仗捉迷藏還沒玩夠,錦繡未央關(guān)鍵時刻擾亂我,洗臉?biāo)⒀礼R馬虎虎應(yīng)付我,該安...
    梓昱閱讀 201評論 0 0
  • 又一個百倍幣的誕生!!VICTEX將在七月上線VT交易 前段時間小編在潛伏的群里被一個三周翻了31倍的幣種刷屏了。...
    神的孩子愛跳5閱讀 544評論 0 0
  • 題記:“夜晚散步,聽到一對情侶指著我的背影說,瞧瞧,你要是不對我好點,你就會像他一樣,永遠(yuǎn)做個單身狗吧!驀然間,我...
    阿多不嫌多閱讀 331評論 0 0

友情鏈接更多精彩內(nèi)容