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

資訊專欄INFORMATION COLUMN

并發(fā)基礎(chǔ)

Warren / 742人閱讀

摘要:關(guān)于,這個(gè)方法只會(huì)喚醒一個(gè)線程,并且不允許指定喚醒哪個(gè)線程,這是可能會(huì)發(fā)生死鎖的。使用不可變對(duì)象降低了垃圾回收所產(chǎn)生的額外開銷,同時(shí)也可以減少一些為了維護(hù)在并發(fā)中的的代碼開銷。

前言

跟著 The Java Tutorials 把并發(fā)的一些基礎(chǔ)過(guò)了一遍,發(fā)現(xiàn)仍然還是有很多不清楚的地方,主要是因?yàn)槠匠]有機(jī)會(huì)實(shí)際應(yīng)用吧,理論知識(shí)要有,實(shí)踐也很重要,哪怕是寫些小 demo 也可以的。

雖然主要是跟著 tutorials 的 concurrency 章節(jié)整理的,但這并不是官方文檔的一個(gè)翻譯哈,看到一個(gè)地方有前置技能不足的時(shí)候,就會(huì)穿插著一些對(duì) API 的學(xué)習(xí)之類的。

還有就是。。segmentfault 對(duì) markdown 的解析和我用的編輯器好像不太一致,引用的地方有點(diǎn)亂,實(shí)在懶得改了。。要是看著不舒服,可以看這個(gè) 并發(fā)基礎(chǔ)(一)

Concurrency Foundation 1. Synchronized

Java 提供了兩種基礎(chǔ)的同步語(yǔ)法:1. synchronized 方法 2. synchronized 語(yǔ)句塊

同步是在內(nèi)置鎖(intrinsic lock)或者叫做監(jiān)視鎖(monitor lock)的基礎(chǔ)上建立的(API 中通常將其稱為 monitor),這個(gè) monitor 在同步的兩個(gè)方面發(fā)揮作用: 1. 強(qiáng)制性的多帶帶訪問對(duì)象 2. 建立 happens-before 關(guān)系

每個(gè)對(duì)象都有自己的內(nèi)置鎖,如果有線程想要訪問這個(gè)對(duì)象,需要先獲取這個(gè)對(duì)象的內(nèi)置鎖,那么此時(shí)其他線程是無(wú)法獲取這個(gè)鎖的,也就是無(wú)法訪問這個(gè)對(duì)象,直至先前的線程釋放鎖。

synchronized 就是 Java 對(duì)內(nèi)置鎖的支持。

synchronized 方法

當(dāng)線程調(diào)用 synchronized 方法時(shí),就會(huì)自動(dòng)獲取該方法對(duì)象的 synchronized 鎖,返回時(shí)才會(huì)釋放,即使是由沒被 catch 的異常返回的,也會(huì)釋放鎖。

那么,對(duì)于 static synchronized method 呢?這個(gè)讓人有些迷惑,因?yàn)?static method 是和類關(guān)聯(lián)的,而不是對(duì)象。

Intrinsic Locks and Synchronization (The Java? Tutorials > Essential Classes > Concurrency)

In this case, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class"s static fields is controlled by a lock that"s distinct from the lock for any instance of the class.

根據(jù)文檔的描述,這種情況下,對(duì)類的靜態(tài)域的訪問和對(duì)類的普通實(shí)例的訪問所獲取的鎖是不同的。

其實(shí)就是類鎖和對(duì)象鎖的區(qū)別,類鎖也就是 static synchronized method 和 synchronized(xx.class){} 所使用的鎖,對(duì)象鎖就是 non-static synchronized method 和 synchronized(xxx){} 所使用的鎖。

因?yàn)檫@兩個(gè)鎖是不同的,所以如果對(duì)同一個(gè)類 A,線程 1 想獲取類 A 對(duì)象實(shí)例的鎖,和線程 2 想獲取類 A 的類鎖,這兩個(gè)線程是不存在競(jìng)爭(zhēng)關(guān)系的。

下面看看 synchronized method 的具體作用:

作用:

同一個(gè)對(duì)象上的兩個(gè) synchronized 方法的調(diào)用不會(huì)交替進(jìn)行,只有一個(gè)線程釋放后其他線程才能獲取這個(gè)鎖

當(dāng)一個(gè) synchronized 方法退出后,它會(huì)自動(dòng)和后面再對(duì)這個(gè)對(duì)象的 synchrnoized 方法的調(diào)用建立一個(gè) happens-before 關(guān)系,這可以保證這個(gè)對(duì)象的狀態(tài)的改變對(duì)其他線程都可見。

需要注意的是,構(gòu)造器是不能被同步的,這是語(yǔ)法上面的要求,這好理解,構(gòu)造器本來(lái)就是只有創(chuàng)建對(duì)象的線程才能訪問,所以不需要同步。

這個(gè) warning 值得注意

Synchronized Methods (The Java? Tutorials > Essential Classes > Concurrency)

Warning:?When constructing an object that will be shared between threads, be very careful that a reference to the object does not "leak" prematurely. For example, suppose you want to maintain a List called instances containing every instance of class. You might be tempted to add the following line to your constructor:

instances.add(this);

But then other threads can use instances to access the object before construction of the object is complete.

這是在多線程中很容易忽略的一個(gè)問題,就是在還沒有構(gòu)建對(duì)象完成時(shí),其他線程就已經(jīng)訪問了這個(gè)對(duì)象。

synchronized 算是一種簡(jiǎn)單直接的方式去解決多線程之間的干擾和內(nèi)存一致性錯(cuò)誤,但是可能會(huì)帶來(lái)活躍性的問題。

synchronized 語(yǔ)句和可重入的同步

這部分以及活躍性問題在之前的博客里有詳細(xì)寫了 線程安全性

2. Atomic Access

原子性的動(dòng)作是一次完成的

Atomic Access (The Java? Tutorials > Essential Classes > Concurrency)

  • Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
  • Reads and writes are atomic for all variables declared volatile (including long and double variables).

翻譯過(guò)來(lái)就是:

對(duì)引用變量和除了 long 和 double 的基本數(shù)據(jù)類型的讀、寫都是原子操作

對(duì) volatile 修飾的變量的讀、寫也是原子操作。

這段我看了之后有點(diǎn)迷,因?yàn)橹暗母拍钪芯褪?volatile 只能保證被修飾變量的可見性而不能保證原子性,為何文檔中說(shuō) "Reads and writes are atomic for all variables declared volatile"?還有就是,對(duì)引用變量的讀寫是原子操作?

想了想,我覺得他想表達(dá)的意思是這樣的:

首先,文檔中說(shuō)的讀寫,肯定指的是多帶帶的讀、多帶帶的寫,比如 int a = 1; 這句肯定是一個(gè)原子操作,就是向 a 寫入值 1,這很容易看出來(lái)。

可如果是 b = a; 呢?這樣就很容易讓人感到迷惑了,猛然一看,這就是對(duì)引用變量的寫入,但是,它應(yīng)該是分為幾步操作的,1. 讀取 a 2. 寫入 b,如果說(shuō)多帶帶對(duì) a 的讀取和多帶帶對(duì) b 的寫入,這些都是原子操作,文檔中說(shuō)的是沒有錯(cuò)的,可是實(shí)際上是對(duì)于 a 這個(gè)變量引用,它是隨時(shí)有可能被更新的,也就是說(shuō) b = a; 可能被分解為 1. 讀取 a 2. 寫入 b 3. 寫入 a,這個(gè)時(shí)候 b = a; 這個(gè)語(yǔ)句就會(huì)帶來(lái)錯(cuò)誤。

因?yàn)檫@種情況其實(shí)是很容易遇到的,所以給我的印象就是 b = a; 這種對(duì)引用變量的讀寫通常并不是原子操作,所以看到文檔中這段話感到很迷惑,文檔的描述是正確的,只不過(guò)他所界定的原子操作和讀寫的概念,指的是一種最窄的概念,并且,對(duì)于 b = a; 這種對(duì)引用變量的賦值,之所以出現(xiàn)問題,實(shí)際上是因?yàn)閷?duì)變量 a 可見性沒有保證,這就不能讓原子操作背這個(gè)鍋了。

文檔中后面也說(shuō)到了,雖然原子操作是完整的操作,使用它們可以不用擔(dān)心線程干擾,但是有時(shí)仍然需要對(duì)原子操作進(jìn)行同步,因?yàn)榧词故窃硬僮饕脖苊獠涣丝赡艹霈F(xiàn)的內(nèi)存一致性錯(cuò)誤。

Atomic actions cannot be interleaved, so they can be used without fear of thread interference. However, this does not eliminate all need to synchronize atomic actions, because memory consistency errors are still possible.

所以說(shuō),使用簡(jiǎn)單的原子操作訪問會(huì)比用 synchronized 訪問變量會(huì)更有效,但是這就需要我們考慮到內(nèi)存一致性的問題,因此需要使用哪種方式訪問變量,就要看應(yīng)用的規(guī)模和復(fù)雜程度了。

3. Guarded blocks

多線程之間肯定少不了協(xié)同工作,最常見的方式就是使用 Guarded block:

public void guardedJoy() {
    // Simple loop guard. Wastes
    // processor time. Don"t do this!
    while(!joy) {}
    System.out.println("Joy has been achieved!");
}

這就是一個(gè) Guarded block,它會(huì)不斷地檢查 joy 這個(gè)值,而 joy 是由另一個(gè)線程所設(shè)置的。

不過(guò),像上面這樣的循環(huán)等待對(duì)資源也太浪費(fèi)了,可以采用另一種方式:

public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we"re waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

這里使用到了 Object.wait()
Object (Java Platform SE 8 )

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).

它的作用就是讓調(diào)用該方法的線程進(jìn)入 WAITING 狀態(tài),直到有其他線程調(diào)用該對(duì)象的 notify() or notifyAll() 方法

The current thread must own this object"s monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object"s monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

調(diào)用 wait() 之前需要先獲取這個(gè)對(duì)象的鎖,一旦調(diào)用之后就會(huì)釋放對(duì)象的鎖,然后等待,直到其他線程調(diào)用 notify() or notifyAll(),但也不是能讓等待的線程立馬從 wait() 返回,而是要等到調(diào)用 notify() or notifyAll() 的線程釋放鎖之后,等待線程才有機(jī)會(huì)從 wait() 返回,這個(gè)機(jī)會(huì)就是:當(dāng)前線程可以重新獲取鎖的持有權(quán)限,然后才能繼續(xù)執(zhí)行。

還是回到 guardedJoy 那個(gè)例子,這個(gè)版本的 guardedJoy 變成了 synchronized 的了,為什么呢?剛剛介紹 wait() 方法時(shí)說(shuō)到了,一個(gè)線程想要調(diào)用一個(gè)對(duì)象的 wait() 方法首先要獲取到這個(gè)對(duì)象的鎖,而獲取鎖的最簡(jiǎn)單的方式就是在 synchronized 方法里調(diào)用 wait() 啦

那么,當(dāng) Thread1 調(diào)用 wait() 時(shí),就會(huì)釋放鎖然后掛起。當(dāng)其他線程 Thread2 請(qǐng)求獲取這個(gè)鎖并成功后調(diào)用 notifyAll() 通知等待的所有線程,在 Thread2 釋放這個(gè)鎖后,等待的線程 Thread1 再次申請(qǐng)就可以重新獲得這個(gè)鎖,之后就可以從 wait() 方法返回繼續(xù)執(zhí)行了。

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}

關(guān)于 notify(),這個(gè)方法只會(huì)喚醒一個(gè)線程,并且不允許指定喚醒哪個(gè)線程,這是可能會(huì)發(fā)生死鎖的。以生產(chǎn)者消費(fèi)者問題為例,假設(shè)分別有 2 個(gè)consumer 和 producer,緩沖區(qū)大小為 1,有可能你喚醒的那個(gè)線程可能恰好是相同角色的線程,也就是說(shuō)現(xiàn)在可能是一個(gè) consumer 喚醒了另一個(gè) consumer,本來(lái) consumer 想要喚醒的是 producer,緩存區(qū)仍然為空的,但是 producer 卻還在 wait,因?yàn)殄e(cuò)過(guò)了被 consumer 喚醒的機(jī)會(huì),從而就會(huì)產(chǎn)生死鎖。

那么什么時(shí)候可以使用 notify() 呢?文檔中是這樣給的建議:
Guarded Blocks (The Java? Tutorials >Essential Classes > Concurrency)

Note: There is a second notification method, notify, which wakes up a single thread. Because notify doesn"t allow you to specify the thread that is woken up, it is useful only in massively parallel applications — that is, programs with a large number of threads, all doing similar chores. In such an application, you don"t care which thread gets woken up.

所以 notify() 一般只在大規(guī)模并發(fā)應(yīng)用(即系統(tǒng)有大量相似任務(wù)的線程)中使用。因?yàn)閷?duì)于大規(guī)模并發(fā)應(yīng)用,我們其實(shí)并不關(guān)心哪一個(gè)線程被喚醒。

看一下官網(wǎng)給的生產(chǎn)者消費(fèi)者的示例程序:

數(shù)據(jù)通過(guò) Drop 對(duì)象共享信息:

public class Drop {
    // Message sent from producer
    // to consumer.
    private String message;
    // true 表示為空,需要等待生產(chǎn)數(shù)據(jù)
    // false 表示可以取數(shù)據(jù)了
    private boolean empty = true; 

    public synchronized String take() {
        // Wait until message is
        // available.
        while (empty) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        // Toggle status.
        empty = true;
        // Notify producer that
        // status has changed.
        notifyAll();
        return message;
    }

    public synchronized void put(String message) {
        // Wait until message has
        // been retrieved.
        while (!empty) {
            try { 
                wait();
            } catch (InterruptedException e) {}
        }
        // Toggle status.
        empty = false;
        // Store message.
        this.message = message;
        // Notify consumer that status
        // has changed.
        notifyAll();
    }
}

生產(chǎn)者:

import java.util.Random;

public class Producer implements Runnable {
    private Drop drop;

    public Producer(Drop drop) {
        this.drop = drop;
    }

    public void run() {
        String importantInfo[] = {
            "Mares eat oats",
            "Does eat oats",
            "Little lambs eat ivy",
            "A kid will eat ivy too"
        };
        Random random = new Random();

        for (int i = 0;
             i < importantInfo.length;
             i++) {
            drop.put(importantInfo[i]);
            try {
                // 隨機(jī)的暫停一段時(shí)間,接近實(shí)際中情況
                Thread.sleep(random.nextInt(5000));
            } catch (InterruptedException e) {}
        }
        drop.put("DONE");
    }
}

消費(fèi)者:

import java.util.Random;

public class Consumer implements Runnable {
    private Drop drop;

    public Consumer(Drop drop) {
        this.drop = drop;
    }

    public void run() {
        Random random = new Random();
        for (String message = drop.take();
             ! message.equals("DONE");
             message = drop.take()) {
            System.out.format("MESSAGE RECEIVED: %s%n", message);
            try {
                Thread.sleep(random.nextInt(5000));
            } catch (InterruptedException e) {}
        }
    }
}

主線程

public class ProducerConsumerExample {
    public static void main(String[] args) {
        Drop drop = new Drop();
        (new Thread(new Producer(drop))).start();
        (new Thread(new Consumer(drop))).start();
    }
}
4. Immutable Objects

在并發(fā)編程中,一種被普遍認(rèn)可的原則就是:盡可能的使用不可變對(duì)象來(lái)創(chuàng)建簡(jiǎn)單、可靠的代碼。
因?yàn)?immutable objects 創(chuàng)建后不能被修改,所以不會(huì)出現(xiàn)由于線程干擾產(chǎn)生的錯(cuò)誤 or 內(nèi)存一致性錯(cuò)誤。

但有時(shí)候會(huì)有這種擔(dān)憂,就是使用 immutable objects 每次創(chuàng)建新對(duì)象的開銷會(huì)不會(huì)太大,如果不使用 immutable objects,就可以避免創(chuàng)建過(guò)多新對(duì)象,只需要 update 就可以。

而文檔中說(shuō)到,這種創(chuàng)建對(duì)象的開銷常常被過(guò)分高估,因?yàn)槭褂貌豢勺儗?duì)象所帶來(lái)的一些效率提升可以抵消這種開銷。

e.g. 使用不可變對(duì)象降低了垃圾回收所產(chǎn)生的額外開銷,同時(shí)也可以減少一些為了維護(hù)在并發(fā)中的 mutable objects 的代碼開銷。

看一下舉得這個(gè)例子:

public class SynchronizedRGB {

    // Values must be between 0 and 255.
    private int red;
    private int green;
    private int blue;
    private String name;

    private void check(int red,
                       int green,
                       int blue) {
        if (red < 0 || red > 255
            || green < 0 || green > 255
            || blue < 0 || blue > 255) {
            throw new IllegalArgumentException();
        }
    }

    public SynchronizedRGB(int red,
                           int green,
                           int blue,
                           String name) {
        check(red, green, blue);
        this.red = red;
        this.green = green;
        this.blue = blue;
        this.name = name;
    }

    public void set(int red,
                    int green,
                    int blue,
                    String name) {
        check(red, green, blue);
        synchronized (this) {
            this.red = red;
            this.green = green;
            this.blue = blue;
            this.name = name;
        }
    }

    public synchronized int getRGB() {
        return ((red << 16) | (green << 8) | blue);
    }

    public synchronized String getName() {
        return name;
    }

    public synchronized void invert() {
        red = 255 - red;
        green = 255 - green;
        blue = 255 - blue;
        name = "Inverse of " + name;
    }
}

假如現(xiàn)在 線程1 在執(zhí)行下列代碼,已經(jīng)執(zhí)行到 Statement 1,然后 線程2 恰好也在執(zhí)行,但是 線程2 偏偏是執(zhí)行到 Statement 1 和 2 之間,此時(shí) 線程1 如果再繼續(xù)執(zhí)行 Statement 2,就會(huì)出現(xiàn) getRGB() 和 getName() 結(jié)果不匹配的情況,這就是在并發(fā)中很容易出現(xiàn)的問題。

SynchronizedRGB color =
    new SynchronizedRGB(0, 0, 0, "Pitch Black");
...
int myColorInt = color.getRGB();      //Statement 1
String myColorName = color.getName(); //Statement 2

此時(shí)加上了 synchronized,將 Statement 1 和 2 綁定到了一起,這樣并發(fā)就不會(huì)出問題了。

synchronized (color) {
    int myColorInt = color.getRGB();
    String myColorName = color.getName();
}

會(huì)出現(xiàn)上面這種不匹配的情況,是因?yàn)?color 是一個(gè) mutable object,如果它變成 immutable object,就不會(huì)出現(xiàn)這種問題了。

對(duì)于如何定義 immutable objects,文檔給出了一個(gè) strategy
A Strategy for Defining Immutable Objects (The Java? Tutorials >Essential Classes > Concurrency)

  1. Don"t provide "setter" methods ? methods that modify fields or objects referred to by fields.
  2. Make all fields final and private.
  3. Don"t allow subclasses to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods.
  4. If the instance fields include references to mutable objects, don"t allow those objects to be changed:
    • Don"t provide methods that modify the mutable objects.
    • Don"t share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.

最后一點(diǎn),關(guān)于對(duì) mutable objects 的引用:如果一個(gè)對(duì)外部可變對(duì)象的引用需要被傳到構(gòu)造函數(shù)中,一定不要保存這個(gè)引用,如果必須要保存,就做一個(gè)該對(duì)象的 copy,然后保存這個(gè) copy。類似的,如果是引用內(nèi)部的可變對(duì)象,必要時(shí)也要?jiǎng)?chuàng)建內(nèi)部可變對(duì)象的 copy,以避免在方法中返回原對(duì)象引用。

這一點(diǎn)是很容易被忽略的,在 Core Java 中也提到了類似的情況,作者的建議也是做 copy,不要直接引用原對(duì)象,因?yàn)楹茈y知道這個(gè)對(duì)象引用在其他地方是不是會(huì)被改變。

根據(jù)上面的 strategy,重新定義 RGB:

final public class ImmutableRGB {

    // Values must be between 0 and 255.
    final private int red;
    final private int green;
    final private int blue;
    final private String name;

    private void check(int red,
                       int green,
                       int blue) {
        if (red < 0 || red > 255
            || green < 0 || green > 255
            || blue < 0 || blue > 255) {
            throw new IllegalArgumentException();
        }
    }

    public ImmutableRGB(int red,
                        int green,
                        int blue,
                        String name) {
        check(red, green, blue);
        this.red = red;
        this.green = green;
        this.blue = blue;
        this.name = name;
    }


    public int getRGB() {
        return ((red << 16) | (green << 8) | blue);
    }

    public String getName() {
        return name;
    }

    public ImmutableRGB invert() {
        return new ImmutableRGB(255 - red,
                       255 - green,
                       255 - blue,
                       "Inverse of " + name);
    }
}
High Level Concurrency Objects 1. Lock Objects

在 liveness 那一節(jié)中有舉了一個(gè) Alphonse 和 Gaston 鞠躬產(chǎn)生死鎖的例子,這里產(chǎn)生死鎖的原因在于可能 線程1 進(jìn)入 bow,線程2 進(jìn)入 bowBack,當(dāng) 線程1 進(jìn)入 bow 里的 bowBack 時(shí),線程2 恰好也正在進(jìn)入 bowBack,二者都在等彼此退出,但是卻又永遠(yuǎn)不會(huì)退出,因?yàn)楸舜嗽谘h(huán)等待,會(huì)一直阻塞在這里,從而產(chǎn)生死鎖(循環(huán)等待、不可剝奪、獨(dú)自占有、保持請(qǐng)求)。

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s"
                + "  has bowed to me!%n", 
                this.name, bower.getName());
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s"
                + " has bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse =
            new Friend("Alphonse");
        final Friend gaston =
            new Friend("Gaston");
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

在這一節(jié)作者用 lock objects 來(lái)解決這個(gè)問題

先簡(jiǎn)單了解一下 Lock 這個(gè)接口:

Lock Objects (The Java? Tutorials >Essential Classes > Concurrency)

Lock objects work very much like the implicit locks used by synchronized code. As with implicit locks, only one thread can own a Lock object at a time. Lock objects also support a wait/notify mechanism, through their associated Condition objects.

Lock 很像同步代碼使用的內(nèi)置鎖,在同一時(shí)刻只有一個(gè)線程可以獲得 Lock 對(duì)象。通過(guò)關(guān)聯(lián) Condition 對(duì)象,Lock 對(duì)象也支持 wait/notify 機(jī)制。

關(guān)于 Condition:
Condition (Java Platform SE 8 )

Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.

在 Lock 取代了 synchronized 方法和語(yǔ)句的地方,Condition 相應(yīng)地取代了 Object 監(jiān)視器方法(wait, notify and notifyAll)的使用。

看一下 tryLock() 這個(gè)方法:
Lock (Java Platform SE 8 )

  • boolean?tryLock()
    Acquires the lock only if it is free at the time of invocation.

    Acquires the lock if it is available and returns immediately
    with the value true.
    If the lock is not available then this method will return
    immediately with the value false.

    A typical usage idiom for this method would be:

     
    Lock lock = ...;
    if (lock.tryLock()) {
    try {
     // manipulate protected state

    } finally {

     lock.unlock();

    }
    } else {
    // perform alternative actions
    }


This usage ensures that the lock is unlocked if it was acquired, and
doesn"t try to unlock if the lock was not acquired.

Returns:
true if the lock was acquired and false otherwise

它會(huì)嘗試獲取鎖,如果成功,立刻返回 true,如果失敗也會(huì)立刻返回 false,API 文檔中舉得那個(gè)例子就是一個(gè)典型的用法,這種使用 tryLock() 的方式可以確保在獲取鎖后一定會(huì)釋放鎖,因?yàn)?Lock 和 synchronized 有一個(gè)不一樣的地方在于 synchronized 會(huì)自動(dòng)釋放,而 Lock 不會(huì),所以一定要保證手動(dòng)釋放 Lock 鎖,并且這種方式也可以保證如果沒獲取鎖的情況不會(huì) unlock()。

下面看怎么用 Lock 解決鞠躬的死鎖問題:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.Random;

public class Safelock {
    // 讓 Friend 作為一個(gè)靜態(tài)內(nèi)部類,因?yàn)椴恍枰猛獠孔兞?    static class Friend {
        private final String name;
        // 這里使用的是可重入鎖
        private final Lock lock = new ReentrantLock();

        public Friend(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }

        // 即將到來(lái)的鞠躬
        public boolean impendingBow(Friend bower) {
            Boolean myLock = false;
            Boolean yourLock = false;
            try {
                myLock = lock.tryLock();
                yourLock = bower.lock.tryLock();
            } finally {
                // 如果都獲取到了鎖 or 都沒獲取到,就不會(huì)釋放鎖
                // 如果只有一個(gè)獲取到了,需要釋放
                if (! (myLock && yourLock)) {
                    if (myLock) {
                        lock.unlock();
                    }
                    if (yourLock) {
                        bower.lock.unlock();
                    }
                }
            }
            // 只有兩個(gè)鎖都獲取到時(shí)才會(huì)返回 true
            return myLock && yourLock;
        }
            
        public void bow(Friend bower) {
            // 如果都獲取到了鎖
            if (impendingBow(bower)) {
                try {
                    System.out.format("%s: %s has"
                        + " bowed to me!%n", 
                        this.name, bower.getName());
                    bower.bowBack(this);
                } finally {
                    lock.unlock();
                    bower.lock.unlock();
                }
            } else {
                // 如果只獲取到了一個(gè)鎖,說(shuō)明其他線程搶占了另一個(gè)鎖
                // 在這個(gè)情景中就是 Alphonse 鞠躬之前發(fā)現(xiàn)在 Gaston 正要鞠躬
                // 因此自己就不鞠躬了,從而避免了死鎖
                System.out.format("%s: %s started"
                    + " to bow to me, but saw that"
                    + " I was already bowing to"
                    + " him.%n",
                    this.name, bower.getName());
            }
        }

        public void bowBack(Friend bower) {
            System.out.format("%s: %s has" +
                " bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    // 同樣地,也是將 BowLoop 作為靜態(tài)內(nèi)部類
    static class BowLoop implements Runnable {
        private Friend bower;
        private Friend bowee;

        public BowLoop(Friend bower, Friend bowee) {
            this.bower = bower;
            this.bowee = bowee;
        }
    
        public void run() {
            Random random = new Random();
            for (;;) {
                try {
                    Thread.sleep(random.nextInt(10));
                } catch (InterruptedException e) {}
                bowee.bow(bower);
            }
        }
    }
            

    public static void main(String[] args) {
        final Friend alphonse =
            new Friend("Alphonse");
        final Friend gaston =
            new Friend("Gaston");
        new Thread(new BowLoop(alphonse, gaston)).start();
        new Thread(new BowLoop(gaston, alphonse)).start();
    }
}
2. Executors

文檔教程只是一些比較簡(jiǎn)單的介紹,先過(guò)一遍了。

2.1 Executor Interfaces

concurrent 包中主要有三個(gè) Executor 接口

Executor

ExecutorService

ScheduledExecutorService

Executor

運(yùn)行新任務(wù)。
只有一個(gè) execute() 方法,用來(lái)創(chuàng)建線程。但是這個(gè)方法沒有定義具體的實(shí)現(xiàn)方式,所以對(duì)于不同的 Executor 的實(shí)現(xiàn),有不同的創(chuàng)建方式。
Executor (Java Platform SE 8 )

  • execute

    void?execute(Runnable?command)
    Executes the given command at some time in the future. The command may execute in a new thread, in a pooled thread, or in the calling
    thread, at the discretion of the Executor implementation.
    Parameters:
    command - the runnable task
    Throws:
    RejectedExecutionException - if this task cannot be
    accepted for execution
    NullPointerException - if command is null

execute() 接受一個(gè) Runnable 對(duì)象。

ExecutorService

ExecutorService 除了提供 execute() 方法還提供了 submit() 方法,這個(gè)方法不僅可以接受 Runnable 對(duì)象還可以接受 Callable 對(duì)象。Callable 對(duì)象可以使任務(wù)返還執(zhí)行的結(jié)果
Callable (Java Platform SE 8 )

  • call

    V?call() throws Exception
    Computes a result, or throws an exception if unable to do so.
    Returns:
    computed result
    Throws:
    Exception - if unable to compute a result

call() 方法會(huì)返回計(jì)算的結(jié)果。

通過(guò) submit() 方法返回的 Future 對(duì)象可以讀取 Callable 任務(wù)的執(zhí)行結(jié)果,或是管理 Callable 任務(wù)和 Runnable 任務(wù)的狀態(tài)。

關(guān)于 Future 這個(gè)接口,它是表示異步計(jì)算的結(jié)果,提供的方法是用來(lái) 1. 檢查計(jì)算是否完成 2. 是否等待計(jì)算完成 3. 是否查找計(jì)算的結(jié)果

簡(jiǎn)單使用的例子:

 interface ArchiveSearcher {
    String search (String target);
 }

 class App {

    ExecutorService executor = ...
    ArchiveSearcher searcher = ...

    void showSearch(final String target) throws InterruptedException {

        Future future = executor.submit(new Callable() {
            public String call() {
                return searcher.search(target);
            }
        });

        displayOtherThings(); // do other things while searching

        try {
            // 計(jì)算完成時(shí)才能用 get() 獲取結(jié)果,如果沒有計(jì)算完成會(huì)阻塞著
            displayText(future.get()); // use future
        } catch (ExecutionException ex) {
            cleanup();
            return;
        }

    }
    
 }

submit 里的 Callable 對(duì)象也可以替換成如下代碼:

 FutureTask future =
   new FutureTask(new Callable() {
     public String call() {
       return searcher.search(target);
   }});
 executor.execute(future);

因?yàn)?FutureTask 是實(shí)現(xiàn)了 Runnable 接口的 Future 的實(shí)現(xiàn),所以可以由 Executor 來(lái)執(zhí)行。

public class FutureTask 
extends Object 
implements RunnableFuture
public interface RunnableFuture
extends Runnable, Future

ExecutorService 也提供了批量運(yùn)行 Callable 任務(wù)的方法。最后,ExecutorService 還提供了一些關(guān)閉執(zhí)行器的方法。如果需要支持即時(shí)關(guān)閉,執(zhí)行器所執(zhí)行的任務(wù)需要正確處理中斷。

ScheduledExecutorService

擴(kuò)展了 ExecutorService 接口,添加了 schedule 方法。

public interface ScheduledExecutorService
extends ExecutorService

通過(guò) schedule 方法可以讓命令在給定延遲的時(shí)間之后執(zhí)行或者定期執(zhí)行。

2.2 Thread Pools

這里只是對(duì)線程池的一個(gè)簡(jiǎn)單的介紹。

大多數(shù) concurrent 包里的 executor 的實(shí)現(xiàn)都使用了線程池(由 worker 線程組成),worker 線程獨(dú)立于它所執(zhí)行的 Runnable 任務(wù)和 Callable 任務(wù),并且經(jīng)常用來(lái)執(zhí)行多個(gè)任務(wù)。

使用 worker 線程可以使創(chuàng)建線程的開銷最小化。在大規(guī)模并發(fā)應(yīng)用中,創(chuàng)建大量的Thread對(duì)象會(huì)占用占用大量系統(tǒng)內(nèi)存,分配和回收這些對(duì)象會(huì)產(chǎn)生很大的開銷。

一種最常見的線程池是 fixed thread pool(固定大小的線程池)。這種線程池始終有一定數(shù)量的線程在運(yùn)行,如果一個(gè)線程由于某種原因終止運(yùn)行了,線程池會(huì)自動(dòng)創(chuàng)建一個(gè)新的線程來(lái)代替它。需要執(zhí)行的任務(wù)通過(guò)一個(gè)內(nèi)部隊(duì)列提交給線程,當(dāng)沒有更多的工作線程可以用來(lái)執(zhí)行任務(wù)時(shí),隊(duì)列保存額外的任務(wù)。 使用 fixed thread pool 的一個(gè)很重要的好處是可以 degrade gracefully。

比如一個(gè) Web 服務(wù)器,每一個(gè) HTTP 請(qǐng)求都是由一個(gè)多帶帶的線程來(lái)處理,不可能為每一個(gè) HTTP 請(qǐng)求都創(chuàng)建一個(gè)新線程,這樣的話當(dāng)系統(tǒng)的開銷超出其能力時(shí),會(huì)突然地對(duì)所有請(qǐng)求都停止響應(yīng)。如果限制 Web 服務(wù)器可以創(chuàng)建的線程數(shù)量,那么它就不必立即處理所有收到的請(qǐng)求,而是在有能力處理請(qǐng)求時(shí)才處理。

創(chuàng)建一個(gè)使用 fixed thread pool 的 executor 的最簡(jiǎn)單的方法是調(diào)用 java.util.concurrent.Executors 的 newFixedThreadPool 工廠方法。

Executors 還提供了下面的工廠方法:

newCachedThreadPool

newSingleThreadExecutor

還有一些創(chuàng)建 ScheduledExecutorService executor 的方法。

還有其他的,如 ThreadPoolExecutor or ScheduledThreadPoolExecutor

2.3 Fork/Join

Basic use:

if (my portion of the work is small enough)
  do the work directly
else
  split my work into two pieces
  invoke the two pieces and wait for the results

思想有點(diǎn)像分而治之的感覺,主要是可以充分利用多處理器系統(tǒng)的并行處理能力。

文檔中舉了一個(gè)圖片模糊處理的例子:

假設(shè)你想要模糊一張圖片。原始的 source 圖片由一個(gè)整數(shù)的數(shù)組表示,每個(gè)整數(shù)表示一個(gè)像素點(diǎn)的顏色數(shù)值。與 source 圖片相同,模糊之后的 destination 圖片也由一個(gè)整數(shù)數(shù)組表示。 對(duì)圖片的模糊操作是通過(guò)對(duì) source 數(shù)組中的每一個(gè)像素點(diǎn)進(jìn)行處理完成的。

處理的過(guò)程:將每個(gè)像素點(diǎn)的色值取出,與周圍像素的色值(紅、黃、藍(lán))放在一起取平均值,得到的結(jié)果被放入 destination 數(shù)組。

因?yàn)橐粡垐D片會(huì)由一個(gè)很大的數(shù)組來(lái)表示,所以處理圖片過(guò)程可能會(huì)很耗時(shí),但是如果使用 fork/join 框架來(lái)完成,就可以充分利用多處理器系統(tǒng)的并行處理能力,加快處理速度。

public class ForkBlur extends RecursiveAction {
    private int[] mSource;
    private int mStart;
    private int mLength;
    private int[] mDestination;
  
    // Processing window size; should be odd.
    private int mBlurWidth = 15;
  
    public ForkBlur(int[] src, int start, int length, int[] dst) {
        mSource = src;
        mStart = start;
        mLength = length;
        mDestination = dst;
    }

    protected void computeDirectly() {
        int sidePixels = (mBlurWidth - 1) / 2;
        for (int index = mStart; index < mStart + mLength; index++) {
            // Calculate average.
            float rt = 0, gt = 0, bt = 0;
            for (int mi = -sidePixels; mi <= sidePixels; mi++) {
                int mindex = Math.min(Math.max(mi + index, 0),
                                    mSource.length - 1);
                int pixel = mSource[mindex];
                rt += (float)((pixel & 0x00ff0000) >> 16)
                      / mBlurWidth;
                gt += (float)((pixel & 0x0000ff00) >>  8)
                      / mBlurWidth;
                bt += (float)((pixel & 0x000000ff) >>  0)
                      / mBlurWidth;
            }
          
            // Reassemble destination pixel.
            int dpixel = (0xff000000     ) |
                   (((int)rt) << 16) |
                   (((int)gt) <<  8) |
                   (((int)bt) <<  0);
            mDestination[index] = dpixel;
        }
    }
  
  ...

現(xiàn)在實(shí)現(xiàn)抽象方法 compute(),在處理時(shí),可以直接計(jì)算,也可以將其分開計(jì)算,取決于一個(gè)閾值,這個(gè)閾值可以簡(jiǎn)單地用數(shù)組的長(zhǎng)度來(lái)代表。

protected static int sThreshold = 100000;

protected void compute() {
    if (mLength < sThreshold) {
        computeDirectly();
        return;
    }
    
    int split = mLength / 2;
    
    invokeAll(new ForkBlur(mSource, mStart, split, mDestination),
              new ForkBlur(mSource, mStart + split, mLength - split,
                           mDestination));
}

因?yàn)?fork/join 的核心就是 ForkJoinPool 類,F(xiàn)orkJoinPool 可以執(zhí)行 ForkJoinTask 任務(wù),而 RecursiveAction 繼承了 ForkJoinTask。所以上面這個(gè)方法如果是在 RecursiveAction 類中,就可以在 ForkJoinPool 中設(shè)置任務(wù)并令其執(zhí)行。

// source image pixels are in src
// destination image pixels are in dst
ForkBlur fb = new ForkBlur(src, 0, src.length, dst);

// Create the ForkJoinPool that will run the task.
ForkJoinPool pool = new ForkJoinPool();

// Run the task.
pool.invoke(fb);
參考資料

The Java? Tutorials

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

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

相關(guān)文章

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

0條評(píng)論

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