Java8內置了強大的多核支持,咱們在處理數據的時候,若是不充分利用多核,都好不意思跟老闆打招呼。
html
咱們常常會使用AtomicInteger來作計數器,以下所示:java
List<String> words = Files.readAllLines(Paths.get("src/main/resources/dic.txt")); AtomicInteger i = new AtomicInteger(); words.parallelStream().forEach(word -> { //獲取word的同義詞、反義詞以及相關詞 //...... LOGGER.info("進度:" + total + "/" + i.incrementAndGet() + " 來自線程:" + Thread.currentThread()); });
在這段代碼中,咱們須要注意兩點,一是parallelStream,二是變量i。git
parallelStream的使用表示forEach中的代碼段有可能會在不一樣線程中併發執行,所以變量i的incrementAndGet方法要保證是原子操做,不然計數器的數據就可能會出錯。github
沒啥問題,一切都還很美好,so far so nice。api
有一天,咱們的需求複雜了,咱們須要的計數器不單單只是+1,而是要支持小數,如2.5,3.1等等,這有什麼大不了的,咱們把AtomicInteger換成AtomicFloat不就支持小數了嗎?併發
接着咱們翻遍了JDK類庫,都沒有找到AtomicFloat,怎麼回事呢?oracle
最後終於在java.util.concurrent.atomic的package-summary.html頁面的最後部分發現了祕密:app
Additionally, classes are provided only for those types that are commonly useful in intended applications. For example, there is no atomic class for representing byte
. In those infrequent cases where you would like to do so, you can use an AtomicInteger
to hold byte
values, and cast appropriately. You can also hold floats using Float.floatToRawIntBits(float)
andFloat.intBitsToFloat(int)
conversions, and doubles using Double.doubleToRawLongBits(double)
andDouble.longBitsToDouble(long)
conversions.ide
接下來咱們就能夠利用AtomicInteger做爲基礎來實現本身的AtomicFloat了,實現AtomicDouble和AtomicByte也是相似的作法,下面看看在word分詞中實現的AtomicFloat:ui
package org.apdplat.word.util; import java.util.concurrent.atomic.AtomicInteger; /** * 由於Java沒有提供AtomicFloat * 因此本身實現一個 * @author 楊尚川 */ public class AtomicFloat extends Number { private AtomicInteger bits; public AtomicFloat() { this(0f); } public AtomicFloat(float initialValue) { bits = new AtomicInteger(Float.floatToIntBits(initialValue)); } public final float addAndGet(float delta){ float expect; float update; do { expect = get(); update = expect + delta; } while(!this.compareAndSet(expect, update)); return update; } public final float getAndAdd(float delta){ float expect; float update; do { expect = get(); update = expect + delta; } while(!this.compareAndSet(expect, update)); return expect; } public final float getAndDecrement(){ return getAndAdd(-1); } public final float decrementAndGet(){ return addAndGet(-1); } public final float getAndIncrement(){ return getAndAdd(1); } public final float incrementAndGet(){ return addAndGet(1); } public final float getAndSet(float newValue) { float expect; do { expect = get(); } while(!this.compareAndSet(expect, newValue)); return expect; } public final boolean compareAndSet(float expect, float update) { return bits.compareAndSet(Float.floatToIntBits(expect), Float.floatToIntBits(update)); } public final void set(float newValue) { bits.set(Float.floatToIntBits(newValue)); } public final float get() { return Float.intBitsToFloat(bits.get()); } public float floatValue() { return get(); } public double doubleValue() { return (double) floatValue(); } public int intValue() { return (int) get(); } public long longValue() { return (long) get(); } public String toString() { return Float.toString(get()); } }
相似的狀況也發生在了FloatStream身上,咱們在JDK類庫中也找不到FloatStream,看下面這段描述:
There are primitive-specialized versions of Stream for ints, longs, and doubles: IntStream, LongStream, DoubleStream. There are not primitive versions for the rest of the primitive types because it would have required an unacceptable amount of bloat in the JDK. IntStream, LongStream, and DoubleStream were deemed useful enough to include, and streams of other numeric primitives can represented using these three via widening primitive conversion.