• Java并发--线程安全性


    1、主要内容

    2、基础知识讲解

    3、线程安全性

     

    1、主要内容

    2、基础知识讲解

    2.1  CPU多级缓存

      缓存一致性 MESI

    2.2 Java内存模型(JMM)

    3、线程安全性 

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

    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
    @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就可以对其进行重排序

  • 相关阅读:
    Windows8 游戏开发教程开篇
    IPAD 游戏开发方案,windows开发
    谈一谈 Windows 8 的软件开发架构
    html5 大家一起来瞅吧瞅吧
    silverlight5中CLGF的推进
    html5 游戏界面问题
    挑战SVN,最纯洁的SVN客户端计划
    章鱼哥的暴走,HTML5也开始了
    MySQL的常用操作!
    两种屏蔽鼠标右键的方法
  • 原文地址:https://www.cnblogs.com/panlei3707/p/10998144.html
Copyright © 2020-2023  润新知