Java并发--线程安全性

一、主要内容

二、基础知识讲解

三、线程安全性

 

 

一、主要内容

二、基础知识讲解

2.1  CPU多级缓存

  缓存一致性 MESIjava

2.2 Java内存模型(JMM)

 

三、线程安全性 

 

 3.1 原子性-Atomic包    package java.util.concurrent.atomic;

给定一个模拟并发访问的例子,不作任何的处理缓存

@Slf4j
@NotThreadSafe
public class CountExample1 {

    // 请求总数
    public static int clientTotal = 5000;

    // 同时并发执行的线程数
    public static int threadTotal = 200;

    public static int count = 0;

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }

    private static void add() {
        count++;
    }
}
CountExample1

这里面模拟共有5000个请求,并发请求是200个,而后一次请求就将count加一,运行的话会发现count的值比5000小安全

如今咱们对上面代码进行修改,将count进行原子性并发

@Slf4j
@NotThreadSafe
public class AtomicExample1 {

    // 请求总数
    public static int clientTotal = 5000;

    // 同时并发执行的线程数
    public static int threadTotal = 200;

    public static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }

    private static void add() {
        count.incrementAndGet(); // count.getAndIncrement();
    }
}

这里count是AtomicInteger类型,咱们使用了incrementAndGet()这个方法,这里进行源码分析:app

incrementAndGet()调用了unsafe类的getAndAddInt()方法,三个参数分别是count自身,count当前值,加法运算的加数ide

咱们继续查看getAndAddInt()的源码源码分析

主体实现是do...while...,这里的getIntVolatile()方法是native方法,它是从主内存中获取count所表明的数的值;而后是compareAndSwapInt()方法,也是native方法,该方法先会取得var2的值,而后比较var2与var5,也就是当前线程工做内存中的count值 与 主内存中的值,若是相等,就将var5 + var4(也就是1),赋值给工做内存;不然,从新取var5,var2比较。而后返回var5,旧值。ui

CASatom

 1)AtomicIntegerFiledUpdater类spa

该类的核心做用是原子性更新一个类指定的一个字段的值,该字段必须是volatile修饰,且非static

@Slf4j
@ThreadSafe
public class AtomicExample2 {
    private static AtomicIntegerFieldUpdater<AtomicExample2> updater =
            AtomicIntegerFieldUpdater.newUpdater(AtomicExample2.class, "count");

    @Getter
    public volatile int count = 100;

    public static void main(String[] args) {
        AtomicExample2 example2 = new AtomicExample2();
        if(updater.compareAndSet(example2, 100, 120)) {
            log.info("success, count:{}", example2.getCount());
        }
        if(updater.compareAndSet(example2, 100, 120)) {
            log.info("success, count:{}", example2.getCount());
        } else {
            log.info("failed, count:{}", example2.getCount());
        }
    }
}
AtomicIntegerFieldUpdater

2)AtomicStampedReference类

解决CAS的ABA问题。什么是ABA问题,就是一个线程1在拿线程中的当前值为A与主内存的值比较的时候,另外一个线程2已经将该主存中的值改成B又改回了A,致使线程1的比较成立。但这跟不符合CAS的设计。

因此咱们在一个值每次进行修改的时候,加一个版本号,也就是Stamp,每次再加一个版本号的比较,解决此问题。

3) AtomicBoolean类

保证一段代码只执行一次

private static AtomicBoolean isHappened = new AtomicBoolean(false);

if(isHappened.compareAndSet(false, true)) {
    // 执行的代码
}

 

 3.2 原子性--synchronized  同步,关键字

做用于调用的对象,表示是某一个对象。

 

 3.3 可见性   一个线程对主内存的修改能够及时的被其余线程观察到

解决方法能够用上面提到的synchronized

还可使用volatile 关键字

volatile变量在每次被线程访问时,都强迫从主内存中读取该变量的值,在该变量发生变化的时候,会强迫线程将最新的值刷新到主内存。

 

因此的操做是指令级别的,只须要了解便可

volatile的使用场景:状态标记量

 

3.4 有序性 

若是两个操做的执行顺序没法从happens-before原则推导出来,就没法保证他们的有序性,JVM就能够对其进行重排序

相关文章
相关标签/搜索