各种版本的累加器

x33g5p2x  于2022-04-20 转载在 其他  
字(3.7k)|赞(0)|评价(0)|浏览(228)

一 非线程安全的累加器

1 代码

package concurrent.Accumulator;

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class IntegerAccumulator {
    private int init;

    // 构造时传入输入初始值
    public IntegerAccumulator(int init) {
        this.init = init;
    }

    // 对初始值增加 i
    public int add(int i) {
        this.init += i;
        return this.init;
    }

    // 返回当前的初始值
    public int getValue() {
        return this.init;
    }

    private static void slowly() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // 定义累加器,并且将初始值设置为 0
        IntegerAccumulator accumulator = new IntegerAccumulator(0);
        // 定义三个线程,并且分别启动
        IntStream.range(0, 3).forEach(i -> new Thread(() -> {
            int inc = 0;
            while (true) {
                // 首先获得 old value
                int oldValue = accumulator.getValue();
                // 然后调用 add 方法计算
                int result = accumulator.add(inc);
                System.out.println(oldValue + "+" + inc + "=" + result);
                // 如果不合理,则输出错误的信息
                if (inc + oldValue != result) {
                    System.out.println("ERROR:" + oldValue + "+" + inc + "=" + result);
                }
                inc++;
                slowly();
            }
        }).start());
    }
}

2 测试

0+0=0
0+0=0
0+0=0
0+1=1
2+1=3
1+1=2
3+2=5
3+2=5
3+2=5
8+3=11
5+3=8
11+3=14
14+4=18
14+4=18
18+4=22
27+5=32
22+5=27
27+5=32
32+6=38
38+6=44
44+6=50
57+7=64
50+7=57
57+7=71
ERROR:57+7=71
79+8=87
71+8=79
87+8=95
95+9=104
95+9=104
95+9=104
124+10=134
104+10=114
104+10=124
ERROR:104+10=124

二 同步方式的累加器

1 代码

package concurrent.Accumulator;

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class IntegerAccumulator1 {
    private int init;

    // 构造时传入输入初始值
    public IntegerAccumulator1(int init) {
        this.init = init;
    }

    // 对初始值增加 i
    public int add(int i) {
        this.init += i;
        return this.init;
    }

    // 返回当前的初始值
    public int getValue() {
        return this.init;
    }

    private static void slowly() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // 定义累加器,并且将初始值设置为 0
        IntegerAccumulator1 accumulator = new IntegerAccumulator1(0);
        // 定义三个线程,并且分别启动
        IntStream.range(0, 3).forEach(i -> new Thread(() -> {
            int inc = 0;
            while (true) {
                int oldValue;
                int result;
                // 使用 class 实例作为同步锁
                synchronized (IntegerAccumulator.class){
                    // 首先获得 old value
                    oldValue = accumulator.getValue();
                    // 然后调用 add 方法计算
                    result = accumulator.add(inc);
                }
                System.out.println(oldValue + "+" + inc + "=" + result);
                // 如果不合理,则输出错误的信息
                if (inc + oldValue != result) {
                    System.out.println("ERROR:" + oldValue + "+" + inc + "=" + result);
                }
                inc++;
                slowly();
            }
        }).start());
    }
}

2 测试

0+0=0
0+0=0
0+0=0
1+1=2
0+1=1
2+1=3
3+2=5
5+2=7
7+2=9
12+3=15
9+3=12
15+3=18
18+4=22
26+4=30
22+4=26
30+5=35
35+5=40
40+5=45
57+6=63
45+6=51
51+6=57
63+7=70
77+7=84
70+7=77
84+8=92
100+8=108
92+8=100

3 说明

这里将数据同步的控制放在了线程的逻辑执行单元中,而在 IntegerAccumulator1 中未增加任何同步的控制,如果单纯对 getValue 方法和 add 方法增加同步控制,虽然保证了单个方法的原子性,但是两个原子类型的操作在一起未必就是原子性的,因此在线程的逻辑执行单元中增加同步控制是最为合理的。

三 不可变的累加器

1 代码

package concurrent.Accumulator;

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public final class IntegerAccumulator2 {
    private int init;

    // 构造时传入输入初始值
    public IntegerAccumulator2(int init) {
        this.init = init;
    }

    public IntegerAccumulator2(IntegerAccumulator2 accumulator2, int init) {
        this.init = accumulator2.getValue() + init;
    }

    // 对初始值增加 i
    public IntegerAccumulator2 add(int i) {
        return new IntegerAccumulator2(this, i);
    }

    // 返回当前的初始值
    public int getValue() {
        return this.init;
    }

    private static void slowly() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // 定义累加器,并且将初始值设置为 0
        IntegerAccumulator2 accumulator = new IntegerAccumulator2(0);
        // 定义三个线程,并且分别启动
        IntStream.range(0, 3).forEach(i -> new Thread(() -> {
            int inc = 0;
            while (true) {
                // 首先获得 old value
                int oldValue = accumulator.getValue();
                // 然后调用 add 方法计算
                int result = accumulator.add(inc).getValue();
                System.out.println(oldValue + "+" + inc + "=" + result);
                // 如果不合理,则输出错误的信息
                if (inc + oldValue != result) {
                    System.out.println("ERROR:" + oldValue + "+" + inc + "=" + result);
                }
                inc++;
                slowly();
            }
        }).start());
    }
}

2 测试

0+0=0
0+0=0
0+0=0
0+1=1
0+1=1
0+1=1
0+2=2
0+2=2
0+2=2
0+3=3
0+3=3
0+3=3
0+4=4
0+4=4
0+4=4
0+5=5
0+5=5

相关文章