博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并发--线程安全性
阅读量:7014 次
发布时间:2019-06-28

本文共 4228 字,大约阅读时间需要 14 分钟。

 

 

1、主要内容

2、基础知识讲解

2.1  CPU多级缓存

  缓存一致性 MESI

2.2 Java内存模型(JMM)

 

3、线程安全性 

 

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

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

@Slf4j@NotThreadSafepublic 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@NotThreadSafepublic 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()这个方法,这里进行源码分析:

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

我们继续查看getAndAddInt()的源码

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

CAS

 1)AtomicIntegerFiledUpdater类

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

@Slf4j@ThreadSafepublic class AtomicExample2 {    private static AtomicIntegerFieldUpdater
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就可以对其进行重排序

 

转载于:https://www.cnblogs.com/panlei3707/p/10998144.html

你可能感兴趣的文章
汇编实验五
查看>>
机器学习中矩阵的求导知识
查看>>
JAVA debug 调试demo
查看>>
洛谷P2336 [SCOI2012]喵星球上的点名(后缀数组+莫队)
查看>>
Node.js 搭建HTTP服务器,提供文件下载
查看>>
设计模式学习笔记之模板方法模式
查看>>
Web Service
查看>>
express 3.5 Err: request aborted
查看>>
css推荐
查看>>
点沙成金:半导体芯片(转载)
查看>>
如何进行有效沟通避免出现误会
查看>>
UbuntuFAQ
查看>>
一致性hash与CRUSH算法总结
查看>>
关于pandas精度控制
查看>>
HDU-1069-Monkey and Banana
查看>>
HDU-2795-Billboard
查看>>
[C++/Python] 如何在Python中使用一个DLL? (Windows环境)
查看>>
梳理操作系统概论
查看>>
EasyUI学习总结(一)——EasyUI入门
查看>>
二叉树乘法器
查看>>