国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【J2SE】java并發(fā)編程實(shí)戰(zhàn) 讀書筆記( 一、二、三章)

QLQ / 1643人閱讀

摘要:發(fā)布的對(duì)象內(nèi)部狀態(tài)可能會(huì)破壞封裝性,使程序難以維持不變性條件。不變性線程安全性是不可變對(duì)象的固有屬性之一。可變對(duì)象必須通過安全方式來(lái)發(fā)布,并且必須是線程安全的或者有某個(gè)鎖保護(hù)起來(lái)。

線程的優(yōu)缺點(diǎn)

線程是系統(tǒng)調(diào)度的基本單位。線程如果使用得當(dāng),可以有效地降低程序的開發(fā)和維護(hù)等成本,同時(shí)提升復(fù)雜應(yīng)用程序的性能。多線程程序可以通過提高處理器資源的利用率來(lái)提升系統(tǒng)的吞吐率。與此同時(shí),在線程的使用開發(fā)過程中,也存在著諸多需要考慮的風(fēng)險(xiǎn)。

安全性:有合理的同步下,多線程的并發(fā)隨機(jī)執(zhí)行使線程安全性變得復(fù)雜,如++i

活躍性:在多線程中,常因?yàn)槿鄙儋Y源而處于阻塞狀態(tài),當(dāng)某個(gè)操作不幸造成無(wú)限循環(huán),無(wú)法繼續(xù)執(zhí)行下去的時(shí)候,就會(huì)發(fā)生活躍性問題。

性能:線程總會(huì)帶來(lái)程序的運(yùn)行時(shí)開銷,多線程中,當(dāng)頻繁地出現(xiàn)上下文切換操作時(shí),將會(huì)帶來(lái)極大的開銷。

線程安全性

線程安全的問題著重于解決如何對(duì)狀態(tài)訪問操作進(jìn)行管理,特別是對(duì)共享和可變的狀態(tài)。共享意味著可多個(gè)線程同時(shí)訪問;可變即在變量在其生命周期內(nèi)可以被改變;狀態(tài)就是由某個(gè)類中的成員變量(Field)。

一個(gè)無(wú)狀態(tài)的對(duì)象一定是線程安全的。因?yàn)樗鼪]有可被改變的東西。
public class LoginServlet implements Servlet {
    public void service(ServletRequest req, ServletResponse resp) {
        System.out.println("無(wú)狀態(tài)Servlet,安全的類,沒有字段可操作");
    }
}
原子性

正如我們熟知的 ++i操作,它包含了三個(gè)獨(dú)立的“讀取-修改-寫入”操作序列,顯然是一個(gè)復(fù)合操作。為此java提供了原子變量來(lái)解決 ++i這類問題。當(dāng)狀態(tài)只是一個(gè)的時(shí)候,完全可以勝任所有的情況,但當(dāng)一個(gè)對(duì)象擁有兩個(gè)及以上的狀態(tài)時(shí),仍然存在著需要思考的復(fù)合操作,盡管狀態(tài)都使用原子變量。如下:

public class UnsafeCachingFactorizer implements Servlet {
    private final AtomicReference lastNumber = 
        new AtomicReference();
    private final AtomicReference lastFactors = 
        new AtomicReference();

    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        if (i.equals(lastNumber.get())) {
            encodeIntoResponse(resp, lastFactors.get());
        } else {
            BigInteger[] factors = factor(i);
            lastNumber.set(i);
            lastFactors.set(factors);
            encodeIntoResponse(resp, factors);
        }
    }
} // lastNumber lastFactors 雖然都是原子的,但是 if-else 是復(fù)合操作,屬“先驗(yàn)條件” 

既然是復(fù)合操作,最直接,簡(jiǎn)單的方式就是使用synchronized將這個(gè)方法同步起來(lái)。這種方式能到達(dá)預(yù)期效果,但效率十分低下。

既然提到synchronized加鎖同步,那么就必須知道 鎖的特點(diǎn):

鎖是可以重入的。即子類的同步方法可以調(diào)用本類或父類的同步方法。

同一時(shí)刻,只有一個(gè)線程能夠訪問對(duì)象中的同步方法。

靜態(tài)方法的鎖是 類;普通方法的鎖是 對(duì)象本身。

回顧上面的代碼,一個(gè)方法體中,只要涉及了多個(gè)狀態(tài)的時(shí)候,就一定需要同步整個(gè)方法嗎?答案是否定的,同步只是為了讓多步操作為原子性,即對(duì)復(fù)合操作同步即可,因此需要明確的便是哪些操作是復(fù)合操作。如下:

public class CachedFactorizer implements Servlet {
    private BigInteger lastNumber;
    private BigInteger[] lastFactors;
    private long hits;
    private long cacheHits;
    
    public synchronized long getHits() {
        return hits;
    }
    
    public synchronized double getCacheHitRatio() {
        return (double) cacheHits / (double) hits;
    }
    
    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = null;
        
        synchronized (this) {
            ++hits;
            if (i.equals(lastNumber)) {
                ++cacheHits;
                factors = lastFactors.clone();
            }
        }
        
        if (factors == null) {
            factors = factor(i);
            synchronized (this) {
                lastNumber = 1;
                lastFactors = factors.clone();
            }
        }
        
        encodeIntoResponse(reqsp, factors);
    }
}// 兩個(gè)synchronized分別同步獨(dú)立的復(fù)合操作。
對(duì)象共享

重排序:當(dāng)一個(gè)線程修改對(duì)象狀態(tài)后,其他線程沒有看見修改后的狀態(tài),這種現(xiàn)象稱為“重排序”。

java內(nèi)存模型允許編譯器對(duì)操作順序進(jìn)行重排序,并將數(shù)據(jù)緩存在寄存器中。當(dāng)缺乏同步的情況下,每一個(gè)線程在獨(dú)立的緩存中使用緩存的數(shù)據(jù),并不知道主存中的數(shù)據(jù)已被更改。這就涉及到內(nèi)存可見性的問題。

可見性

內(nèi)存可見性:同步的另一個(gè)重要的方面。我們不僅希望防止多個(gè)線程同時(shí)操作對(duì)象狀態(tài),而且還希望確保某一個(gè)線程修改了狀態(tài)后,能被其他線程看見變化。

volatile:使用 synchronized可以實(shí)現(xiàn)內(nèi)存可見,但java提供了一種稍弱的更輕量級(jí)得同步機(jī)制volatile變量。在訪問volatile變量時(shí)不會(huì)執(zhí)行加鎖操作,因此不會(huì)產(chǎn)生線程阻塞。即便如此還是不能過度使用volatile,當(dāng)且僅當(dāng)能簡(jiǎn)化代碼的實(shí)現(xiàn)以及對(duì)同步策略的驗(yàn)證時(shí),才考慮使用它。

發(fā)布與逸出

發(fā)布指:使對(duì)象能夠在當(dāng)前作用于之外的代碼中使用。即對(duì)象引用能被其他對(duì)象持有。發(fā)布的對(duì)象內(nèi)部狀態(tài)可能會(huì)破壞封裝性,使程序難以維持不變性條件。

逸出指:當(dāng)某個(gè)不應(yīng)該發(fā)布的對(duì)象被發(fā)布時(shí),這種情況被稱為逸出。

// 正確發(fā)布:對(duì)象引用放置公有靜態(tài)域中,所有類和線程都可見
class CarFactory {
    public static Set cars;
    
    private CarFactory() {
        cars = new HashSet();
    }    // 私有,外部無(wú)法獲取 CarFactory的引用
    
    public static Car void newInstance() {    
        Car car = new Car("碰碰車");
        cars.put(car);
        return car;
    }    // 使用方法來(lái)獲取 car
}
// 逸出
class Person {
    private String[] foods = new String[] {"土豆"};
    
    public Person(Event event) {
        person.registListener {
            new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e);
                }
            }
        }
    }// 隱式逸出了this,外界得到了Person的引用 并且 EventListener也獲取了Person的引用。
    
    public String[] getFoods() {
        return foods;
    }// 對(duì)發(fā)布的私有 foods,外界還是可以修改foods內(nèi)部值
}
線程封閉

將可變的數(shù)據(jù)僅放置在單線程中操作的技術(shù),稱之為發(fā)線程封閉。

棧封閉:只能通過局部變量才能訪問對(duì)象。局部變量的固有屬性之一就是封裝在執(zhí)行線程中,它們位于執(zhí)行線程的棧中,其他線程無(wú)法訪問這個(gè)棧,即只在一個(gè)方法內(nèi)創(chuàng)建和使用對(duì)象。

public int test(Person p) {
    int num = 0;
    PersonHolder holder = new PersonHolder();
    
    Person newPerson = deepCopy(p);
    Person woman = holder.getLove(newPerson);
    newPerson.setWomen(person);
    num++;
    
    return num; // 基本類型沒有引用,對(duì)象創(chuàng)建和修改都沒有逸出本方法
}

ThreadLocal類:ThreadLocal能夠使線程中的某個(gè)值與保存值的對(duì)象關(guān)聯(lián)起來(lái)。ThreadLocal提供了 getset等訪問接口的方法,這些方法為每一個(gè)使用該變量的線程都存有一份獨(dú)立的副本,因get總是返回由當(dāng)前執(zhí)行線程在調(diào)用set時(shí)設(shè)置的最新值。

private static ThreadLocal connectionHolder = 
    new ThreadLocal() {
        public Connection initialValue() {
            return DriverManager.getConnection(DB_URL);
        }
    };

public static Connection getConnection() {
    return connectionHolder.get();
}
當(dāng)某個(gè)頻繁執(zhí)行的操作需要一個(gè)臨時(shí)對(duì)象,例如一個(gè)緩沖區(qū),而同時(shí)又希望避免在每次執(zhí)行時(shí)都重新分配該臨時(shí)對(duì)象,就可以使用ThreadLocal。
不變性

線程安全性是不可變對(duì)象的固有屬性之一。不可變對(duì)象一定是線程安全的,它們的不變性條件是由構(gòu)造函數(shù)創(chuàng)建的,只要它們的狀態(tài)不可變。

//    在可變對(duì)象基礎(chǔ)上構(gòu)建不可變類
public final class ThreadStooges {
    private final Set stooges = new HashSet();
    
    public ThreadStooges() {
        stooges.add("Moe");
        stooges.add("Larry");
    }
    
    public boolean isStooge(String name) {
        return stooges.contains(name);
    }
}// 沒有提供可修改狀態(tài)的方式,盡管使用了Set可變集合,但被private final修飾著

對(duì)象不可變的條件

對(duì)象創(chuàng)建以后其狀態(tài)就不能修改。

對(duì)象的所有域都是final類型。

對(duì)象是正確創(chuàng)建的(在對(duì)象的創(chuàng)建期間,this引用沒有逸出)

安全發(fā)布
任何線程都可以在不需要額外同步的情況下安全地訪問不可變對(duì)象,即使在發(fā)布這些對(duì)象時(shí)沒有使用同步。
// 安全的 Holder類
class Holder {
    private int n;
    public Holder(int n) {
        this.n = n;
    }
}

public class SessionHolder {
    // 錯(cuò)誤的發(fā)布,導(dǎo)致 Holder不安全
    public Holder holder;
    
    public void init() {
        holder = new Holder(10);
    }
}// 當(dāng)初始化 holder的時(shí)候,holder.n會(huì)被先默認(rèn)初始化為 0,然后構(gòu)造函數(shù)才初始化為 10;在并發(fā)情況下,可能會(huì)有線程在默認(rèn)初始化 與 構(gòu)造初始化中,獲取到 n 值為 0, 而不是 10;

要安全的發(fā)布一個(gè)對(duì)象,對(duì)象的引用以及對(duì)象的狀態(tài)必須同時(shí)對(duì)其他線程可見。一個(gè)正確構(gòu)造的對(duì)象可以通過以下方式安全發(fā)布:

在靜態(tài)初始化函數(shù)中初始化一個(gè)對(duì)象引用。

將對(duì)象的引用保存到 volatitle 類型的域或者 AtomicReferance 對(duì)象中。

將對(duì)象的引用保存到某個(gè)正確構(gòu)造對(duì)象的 final 類型域中。

將對(duì)象的引用保存到一個(gè)由鎖保護(hù)的域中。

在線程并發(fā)容器中的安全發(fā)布:

通過將一個(gè)鍵或者值放入 Hashtable、synchronizedMap 或者 ConsurrentMap中,可以安全地將它發(fā)布給任何從這些容器中訪問它的線程(無(wú)論是直接訪問還是通過迭代器訪問)。

通過將某個(gè)元素放入 Vector、 CopyOnWriteArrayList、CopyOnWriteArraySet、synchronizedList 或 synchronizedSet中,可以將元素安全地發(fā)布到任何從這些容器中訪問該元素的線程。

通過將某個(gè)元素放入 BlockingQueue或者ConcurrentLinkedQueue中,可以將該元素安全地發(fā)布到任何從這些隊(duì)列中訪問該元素的線程。

通常,要發(fā)布一個(gè)靜態(tài)構(gòu)造的對(duì)象,最簡(jiǎn)單、安全的方式就是使用靜態(tài)的初始化器。如public static Holder holder = new Holder(10)。如果對(duì)象在發(fā)布后狀態(tài)不會(huì)被修改(則稱為事實(shí)不可變對(duì)象),那么在沒有額外的同步情況下,任何線程都可以安全地使用被安全發(fā)布的不可變對(duì)象。

對(duì)象的發(fā)布需求取決于它的可變性:

不可變對(duì)象可以通過任意機(jī)制來(lái)發(fā)布。

事實(shí)不可變對(duì)象必須通過安全方式來(lái)發(fā)布。

可變對(duì)象必須通過安全方式來(lái)發(fā)布,并且必須是線程安全的或者有某個(gè)鎖保護(hù)起來(lái)。

在并發(fā)程序中使用和共享對(duì)象時(shí)可采用的策略:

線程封閉。將對(duì)象封閉在線程中,如在方法中創(chuàng)建和修改局部對(duì)象。

只讀共享。

線程安全共享。對(duì)象內(nèi)部實(shí)現(xiàn)同步,使用公有接口來(lái)訪問。

保護(hù)對(duì)象。使用特定的鎖來(lái)保護(hù)對(duì)象。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/73777.html

相關(guān)文章

  • java 8 實(shí)戰(zhàn)讀書筆記 -第十三章 函數(shù)式的思考

    摘要:當(dāng)我們希望能界定這二者之間的區(qū)別時(shí),我們將第一種稱為純粹的函數(shù)式編程,后者稱為函數(shù)式編程。函數(shù)式編程我們的準(zhǔn)則是,被稱為函數(shù)式的函數(shù)或方法都只能修改本地變量。另一種觀點(diǎn)支持引用透明的函數(shù)式編程,認(rèn)為方法不應(yīng)該有對(duì)外部可見的對(duì)象修改。 一、實(shí)現(xiàn)和維護(hù)系統(tǒng) 1.共享的可變數(shù)據(jù) 如果一個(gè)方法既不修改它內(nèi)嵌類的狀態(tài),也不修改其他對(duì)象的狀態(tài),使用return返回所有的計(jì)算結(jié)果,那么我們稱其為純粹...

    Donne 評(píng)論0 收藏0
  • Java8實(shí)戰(zhàn)》-第三章讀書筆記(Lambda表達(dá)式-01)

    摘要:之前,使用匿名類給蘋果排序的代碼是的,這段代碼看上去并不是那么的清晰明了,使用表達(dá)式改進(jìn)后或者是不得不承認(rèn),代碼看起來(lái)跟清晰了。這是由泛型接口內(nèi)部實(shí)現(xiàn)方式造成的。 # Lambda表達(dá)式在《Java8實(shí)戰(zhàn)》中第三章主要講的是Lambda表達(dá)式,在上一章節(jié)的筆記中我們利用了行為參數(shù)化來(lái)因?qū)Σ粩嘧兓男枨螅詈笪覀円彩褂玫搅薒ambda,通過表達(dá)式為我們簡(jiǎn)化了很多代碼從而極大地提高了我們的...

    longshengwang 評(píng)論0 收藏0
  • java 8 實(shí)戰(zhàn)讀書筆記 -第三章 Lambda表達(dá)式

    摘要:利用前面所述的方法,這個(gè)例子可以用方法引用改寫成下面的樣子構(gòu)造函數(shù)引用對(duì)于一個(gè)現(xiàn)有構(gòu)造函數(shù),你可以利用它的名稱和關(guān)鍵字來(lái)創(chuàng)建它的一個(gè)引用。 第三章 Lambda表達(dá)式 函數(shù)式接口 函數(shù)式接口就是只定義一個(gè)抽象方法的接口,哪怕有很多默認(rèn)方法,只要接口只定義了一個(gè)抽象方法,它就仍然是一個(gè)函數(shù)式接口。 常用函數(shù)式接口 showImg(https://segmentfault.com/img...

    whinc 評(píng)論0 收藏0
  • Java8實(shí)戰(zhàn)》-第三章讀書筆記(Lambda表達(dá)式-02)

    摘要:上下文比如,接受它傳遞的方法的參數(shù),或者接受它的值得局部變量中表達(dá)式需要類型稱為目標(biāo)類型。但局部變量必須顯示的聲明,或?qū)嶋H上就算。換句話說,表達(dá)式只能捕獲指派給它們的局部變量一次。注捕獲實(shí)例變量可以被看作捕獲最終局部變量。 由于第三章的內(nèi)容比較多,而且為了讓大家更好的了解Lambda表達(dá)式的使用,也寫了一些相關(guān)的實(shí)例,可以在Github或者碼云上拉取讀書筆記的代碼進(jìn)行參考。 類型檢查、...

    iflove 評(píng)論0 收藏0
  • Java并發(fā)編程實(shí)戰(zhàn)讀書筆記-第1章 簡(jiǎn)介

    摘要:線程允許同一個(gè)進(jìn)程中同時(shí)存在多個(gè)程序控制流。線程也被稱為輕量級(jí)進(jìn)程。現(xiàn)代操作系統(tǒng)中,都是以線程為基本的調(diào)度單位,而不是進(jìn)程。 并發(fā)簡(jiǎn)史 在早期的計(jì)算機(jī)中不包含操作系統(tǒng),從頭至尾都只執(zhí)行一個(gè)程序,并且這個(gè)程序能訪問計(jì)算機(jī)所有資源。操作系統(tǒng)的出現(xiàn)使得計(jì)算機(jī)每次能運(yùn)行多個(gè)程序,并且不同的程序都在單獨(dú)的進(jìn)程中運(yùn)行:操作系統(tǒng)為各個(gè)獨(dú)立執(zhí)行的進(jìn)程分配內(nèi)存、文件句柄、安全證書等。不同進(jìn)程之間通過一些...

    zhoutk 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<