实现JDK没有提供的AtomicFloat

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分词中实现的AtomicFloatui

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.

相关文章
相关标签/搜索