1、关于伪共享

1.1、回顾:
1、在第三课我们就学习了多核CPU是如何处理“本地高速缓存”与“共享内存“一致性问题,其中就学了一个重要的概念:Cache Line。

2、《Java并发编程的艺术》关于Cache Line的解释:缓存中可以分配的最小存储单位。处理器填写缓存线时会加载整个缓存线,需要使用多个主内存读周期。

3、关键字:“会加载整个缓存线”。
1.2、伪共享是什么?

a、名词:一个缓存行中可能存有多个变量,当多个线程同时修改一个缓存行里面的多个变量时,由于同时只能有一个线程操作缓存行,所以相比较每个变量放到一个缓存行,性能会有所下降,这就是伪共享。
b、解释:如上图,每个Cache Line中都可能存在多个变量,这就是上面说的“会加载整个缓存线”。当一个缓存行中的变量存在资源竞争,其他线程访问其他变量会出现阻塞的情况,可以理解为多个线程互相阻塞,性能会有所下降。
1.3、如何解决?
1、自定义变量占位
// 可能存在高频竞争的变量
private volatile int value;
// 定义占位变量,但不使用。使得value单独占用一个cache line
private int i1;
private long l1, l2, l3, l4, l5, l6, l7;

2、使用注解
@sun.misc.Contented
private volatile int value;

2、关于Actomic*

2.1、实现:

在JDK1.5开始,就提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了更为简单高效、线程安全的方式来更新一个变量的值。例如AtomicBoolean、AtomicLong、AtomicInteger等。

a、原子操作:不可被中断的一个或一系列操作
b、实现原理:循环CAS操作。
    private Unsafe() {}

    private static final Unsafe theUnsafe = new Unsafe();

    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class<?> caller = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(caller.getClassLoader()))
            throw new SecurityException("Unsafe");
        return theUnsafe;
    }
a、Unsafe类是rt.jar包中的类,它提供了原子级别的操作,它的方法都是native方法,通过JNI访问本地的C++库。
b、Unsafe类与synchronize一样是为了解决高并发下的数据安全问题,synchronize获取锁和释放锁是循环CAS的过程,Unsafe类同样也是CAS算法实现乐观锁。
2.2、带来的问题:
a、ABA问题
    CAS只管开头和结尾,也就是头和尾是一样,那就修改成功,中间的这个过程,可能会被人修改过
b、循环/自旋带来的性能开销
c、只能保证一个共享变量的原子操作
    与synchronize比较,synchronize修饰的代码块里可以操作多个共享变量。

3、练习题

3.1 给三个文件,每个文件里面有10万条记录每一行是一个0-100000之间的随机数  我们通过启动三个线程 来统计下这个文件里面大于 95000 数字有多少? 
public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        Long target = 95000L;
        AtomicInteger count = new AtomicInteger();

        List<Long> array1 = getRandomArray();
        List<Long> array2 = getRandomArray();
        List<Long> array3 = getRandomArray();

        Thread thread1 = new Thread(new MyCounter(array1, count, target));
        Thread thread2 = new Thread(new MyCounter(array2, count, target));
        Thread thread3 = new Thread(new MyCounter(array3, count, target));

        thread1.start();
        thread2.start();
        thread3.start();

        thread1.join();
        thread2.join();
        thread3.join();

        System.out.println("统计大于95000的数字共有:" + count.get());
    }
    static class MyCounter implements Runnable{
        private final Long target;
        private final List<Long> data;
        private final AtomicInteger countNum;
        public MyCounter(List<Long> data, AtomicInteger count, Long target) {
            this.data = data;
            this.countNum = count;
            this.target = target;
        }

        @Override
        public void run() {
            for (int i = 0; i < data.size(); i++) {
                if (data.get(i) > target) {
                    countNum.incrementAndGet();
                }
            }
        }
    }
}

// 执行结果:统计大于95000的数字共有:14832

3.2 给三个文件,每个文件里面有10万条记录每一行是一个0-100000之间的随机数 通过三个线程来解析每个文件,找到最大不重复的10个数字?

public class MyTest {
    public static final Integer MAX_SIZE = 10;
    public static final AtomicLongArray longArray = new AtomicLongArray(MAX_SIZE);
    public static void main(String[] args) throws InterruptedException {


        List<Long> array1 = getRandomArray();
        List<Long> array2 = getRandomArray();
        List<Long> array3 = getRandomArray();

        Thread thread1 = new Thread(new MyCounter(array1));
        Thread thread2 = new Thread(new MyCounter(array2));
        Thread thread3 = new Thread(new MyCounter(array3));

        thread1.start();
        thread2.start();
        thread3.start();

        thread1.join();
        thread2.join();
        thread3.join();

        System.out.println("Top 10:" + longArray.toString());
    }

    public static void compareAndSet(Long num) {
        for (int i=0; i< MAX_SIZE; i++){
            boolean continueTag = true;
            do {
                long currentValue = longArray.get(i);
                // 已有重复值,返回
                if (currentValue == num) {
                    return;
                }
                // 没比过当前值,去跟longArray中的下一个值继续比较
                else if (currentValue > num) {
                    continueTag = false;
                }
                // 去执行CAS操作
                else {
                    // 成功了
                    if (longArray.compareAndSet(i, currentValue, num)) {
                        // 淘汰下来的值需要接着跟longArray后面的值比较
                        compareAndSet(currentValue);
                        // 结束当前比较,这里如果不返回,continueTag还是true的,num还会去跟longArray.get(i+1)比较,且可能竞争成功,导致num会替换所有比它小的位置
                        return;
                    }
                }
            } while (continueTag);
        }
    }


    static class MyCounter implements Runnable{
        private final List<Long> data;
        public MyCounter(List<Long> data) {
            this.data = data;
        }

        @Override
        public void run() {
            for (int i = 0; i < data.size(); i++) {
                compareAndSet(data.get(i));
            }
        }
    }
}

// 执行结果:Top 10:[99999, 99998, 99997, 99996, 99995, 99994, 99993, 99992, 99991, 99990]

断水流大师兄
6 声望0 粉丝