CAS

DLC双端锁,CAS,ABA问题

孤人 提交于 2020-01-07 17:48:34
一.什么是DLC双端锁?有什么用处?  为了解决在多线程模式下,高并发的环境中,唯一确保单例模式只能生成一个实例  多线程环境中,单例模式会因为指令重排和线程竞争的原因会出现多个对象 public class DLCDemo { private static DLCDemo instance = null; private DLCDemo(){ System.out.println(Thread.currentThread().getName() + "\t" + " 线程启动"); }; public static DLCDemo getInstance(){ if (instance == null){ instance = new DLCDemo(); } return instance; } public static void main(String[] args) { //多线程模式下 for (int i = 1; i <= 10; i++) { new Thread(() -> { DLCDemo.getInstance(); },String.valueOf(i)).start(); } } } 运行结果: 在10个线程下,出现了10个对象,显然违背了单例模式 改进 public class DLCDemo { /*DLC双端锁机制不一定线程安全

并发编程的基石——CAS机制

爷,独闯天下 提交于 2020-01-07 11:29:35
本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。 并发编程系列博客传送门 Java中提供了很多原子操作类来保证共享变量操作的原子性。这些原子操作的底层原理都是使用了CAS机制。在使用一门技术之前,了解这个技术的底层原理是非常重要的,所以本篇博客就先来讲讲什么是CAS机制,CAS机制存在的一些问题以及在Java中怎么使用CAS机制。 其实Java并发框架的基石一共有两块,一块是本文介绍的CAS,另一块就是AQS,后续也会写博客介绍。 什么是CAS机制 CAS机制是一种数据更新的方式。在具体讲什么是CAS机制之前,我们先来聊下在多线程环境下,对共享变量进行数据更新的两种模式:悲观锁模式和乐观锁模式。 悲观锁更新的方式认为:在更新数据的时候大概率会有其他线程去争夺共享资源,所以悲观锁的做法是:第一个获取资源的线程会将资源锁定起来,其他没争夺到资源的线程只能进入阻塞队列,等第一个获取资源的线程释放锁之后,这些线程才能有机会重新争夺资源。synchronized就是java中悲观锁的典型实现,synchronized使用起来非常简单方便,但是会使没争抢到资源的线程进入阻塞状态,线程在阻塞状态和Runnable状态之间切换效率较低(比较慢)。比如你的更新操作其实是非常快的

Java 多线程与并发(四):CAS

末鹿安然 提交于 2020-01-07 06:38:44
解决线程安全问题,除了上面的 Synchronized 锁之外还有另外一个应用特别广泛的知识点 CAS,可以说 JUC 包完全是建立在 CAS 的基础之上的。 定义 CAS,compare and swap ,是计算机科学中一种实现多线程原子操作的指令,它比较内存中当前存在的值和外部给定的期望值,只有两者相等时,才将这个内存值修改为新的给定值。 CAS操作包含三个操作数,需要读写的内存位置(V)、拟比较的预期原值(A)和拟写入的新值(B),如果V的值和A的值匹配,则将V的值更新为B,否则不做任何操作。 如何解决线程安全问题 前几篇文章多次强调线程安全需要解决的三个问题,原子性,可见性和重排序问题,我们来看看 CAS 能不能解决。 原子性:除了上文介绍的监视器(monitor)的实现,CAS 也能实现读取和更新的原子性操作。 可见性:使用 volatile 关键字来保证。 重排序:使用 volatile 关键字来保证。 所以 CAS + volatile 也能够保证线程安全。 原理 我们拿 AtomicInteger 来看看 CAS 如何在无锁的条件下保证数据的正确性。 private volatile int value; 首先对于我们要操作的变量,需要使用 volatile 关键字,用来保证线程之间的可见性以及防止重排序。这样获取变量的值时就能够直接读取。 public

java并发之非阻塞同步

北慕城南 提交于 2020-01-07 03:30:23
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> 阻塞同步最大的问题是线程阻塞和唤醒带来的性能问题(用户态,内核态的切换),从处理问题的方式来说,阻塞同步属于悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出问题,无论共享数据是否对会出现竞争,它都要加锁。 随着 硬件指令集的发展 ,有了另一种选择: 基于冲突检测的乐观并发策略 ,通俗的讲,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就采取补偿措施(而不是线程阻塞,挂起,该线程仍然持有cpu分配的时间)最常见的补偿措施就是不断重试,直到成功为止,这种方式叫做: 乐观并发策略。 乐观并发策略需要操作和冲突检测这两个步骤具备原子性,所以 硬件指令集的发展 ,让硬件保证一个从语义上看起来需要多次操作的行为只通过一条处理器指令就能完成,这类指令常用的用: 测试并设置(Test-and-Set)。 获取并增加(Fech-and-Increment)。 交换(Swap) 比较并交换(Compare-and-Swap:CAS)。 加载链接/条件存储(Load-Linked/Store-Conditional:LL/SC) JDK1.5之后,Java程序中可以使用CAS操作,该操作由sun.misc.Unsafe类里面的compareAndSwapInt(

Java并发编程---原子操作CAS

≯℡__Kan透↙ 提交于 2020-01-07 01:25:33
实现并发操作的方法有两种:一种是使用锁(Synchronized和Lock),另外一种是使用原子操作(CAS) Synchronized基于阻塞的锁机制可能会带来的问题: a. 被阻塞的线程优先级很高 b.拿到锁的线程一直不释放锁怎么办? c.大量的竞争消耗cpu,同时带来死锁或者其他安全问题 基于上述问题,提出了CAS原子操作 a.CAS原理:利用现代处理器都支持的CAS指令,循环这个指令,直到成功为止 b.CAS(Compare And Swap) : 指令级别上保证这是一个原子操作 c.与CAS相关的三个运算符:一个内存地址V,一个期望值A,一个新值B 基本思路:如果地址V上的值和期望的值A相等,就给地址V赋新值B,如果不是,不做任何操作。 (在(死循环)循环中不断进行CAS操作) d.CAS也会带来一定的问题: 1)ABA问题(A–>B–>A):当第一次取内存地址V时,取到了A的值,可当它要执行CAS操作之前,已经有一个线程对A的值改为B后,再 改为A,这时候的值虽然一样,不过内容已经有过更改。(举例:在现实生活中,你放在座子上的以一杯水被同事喝完,又重新倒满一杯放在那里。很明显,它虽然还是那杯满着的水,可性质上却完全不一样了。) 解决方法:可以通过版本号解决 A1—>B2---->A3 (类似于乐观锁,大多是基于数据版本( Version )记录机制实现。) 2)开销问题

ConcurrentHashMap源码分析

痞子三分冷 提交于 2020-01-07 00:54:39
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> ConcurrentHashMap是一个可以用于并发环境的集合,在jdk8中实现的原理是CAS+synchronized put方法 public V put(K key, V value) { return putVal(key, value, false); } /** Implementation for put and putIfAbsent */ final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException(); int hash = spread(key.hashCode()); int binCount = 0; for (Node<K,V>[] tab = table;;) { Node<K,V> f; int n, i, fh; if (tab == null || (n = tab.length) == 0)//tab 为空 则初始化列表 tab = initTable(); else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {/

Java中的锁[原理、锁优化、CAS、AQS]

血红的双手。 提交于 2020-01-07 00:21:19
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> 1、为什么要用锁? 锁:是为了解决并发操作引起的脏读,导致的数据不一致性的问题 2、锁实现的基本原理 2.1、volatile JAVA编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排它锁单独获取这个变量。Java语言提供了volatile,在某些情况下比锁要更加的方便。 volatile在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能够读到这个修改的值。 结论:如果volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低,因为他不会引起线程的上下文的切换和调度。 2.2、synchronized synchronized通过锁机制实现同步。 先来看下利用synchronized实现同步的基础:JAVA中的每个对象都可以作为锁。 具体表现为以下3种形式: 对于普通同步方法,锁是当前实例对象。 对于静态同步方法,锁是当前类的Class对象。 对于同步方法块,锁是Synchronized括号里的配置对象。 当一个线程试图访问同步代码块时,它首先必须获取锁,退出或抛出异常时必须释放锁。 2.2.1 synchronized实现原理 synchronized是基于Monitor来实现同步的。

Java的CAS乐观锁原理解析

血红的双手。 提交于 2020-01-07 00:20:23
CAS全称 Compare And Swap(比较与交换),在不使用锁的情况下实现多线程之间的变量同步。属于硬件同步原语,处理器提供了基本内存操作的原子性保证。juc包中的原子类就是通过CAS来实现了乐观锁。 CAS算法涉及到三个操作数: 需要读写的内存值 V。 进行比较的旧值A (期望操作前的值) 要写入的新值 B。 当且仅当 V 的值等于 A 时,CAS通过原子方式用新值B来更新V的值(“比较+更新”整体是一个原子操作),否则不会执行任何操作。 一般情况下,“更新”是一个不断重试的过程。 JAVA中的sun.misc.Unsafe类,提供了 compareAndSwapInt compareAndSwapLong 等方法实现CAS。 示例 J.U.C包内的原子操作封装类 看一下AtomicInteger的源码定义: public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe =

SSO手册

时光怂恿深爱的人放手 提交于 2020-01-06 20:52:15
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> SSO手册 背景 在企业发展初期,企业使用的系统很少,通常一个或者两个,每个系统都有自己的登录模块,运营人员每天用自己的账号登录,很方便。 但随着企业的发展,用到的系统随之增多,运营人员在操作不同的系统时,需要多次登录,而且每个系统的账号都不一样,这对于运营人员 来说,很不方便。于是,就想到是不是可以在一个系统登录,其他系统就不用登录了呢?这就是单点登录要解决的问题。 简介 单点登录英文全称Single Sign On,简称就是SSO。它的解释是: 在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。 包括单点登录与单点注销两部分 实现单点登录说到底就是要解决如何产生和存储那个信任,再就是其他系统如何验证这个信任的有效性,因此要点也就以下两个: 存储信任 验证信任 概述 很早期的公司,一家公司可能只有一个Server,慢慢的Server开始变多了。每个Server都要进行注册登录,退出的时候又要一个个退出。用户体验很不好!你可以想象一下,上豆瓣 要登录豆瓣FM、豆瓣读书、豆瓣电影、豆瓣日记......真的会让人崩溃的。我们想要另一种登录体验:一家企业下的服务只要一次注册,登录的时候只要一次登录,退出的时候只要一次退出。怎么做? 一次注册。 一次注册不难

可循环障碍CyclicBarrier 一次性减法CountDownLatch

隐身守侯 提交于 2020-01-06 14:12:02
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> 其实两者的用法异曲同工 先说说 CyclicBarrier ,看看初始化: 有两种初始化,其实很容易理解,第一个是说多少个parties需要执行(通过调用CyclicBarrier.await()来计算线程数量),执行完成后BarrierAction最后收尾执行,第二个就是说多少个线程需要执行(通过调用CyclicBarrier.await()来计算线程数量),执行完成后不需要收尾。 功能 :有多少个线程执行完之后所有线程才能一起执行下一个。 特性:使用AQS和CAS确保线程安全,调用await()会进入condition queue,然后等所有线程执行完成后,也就是count增加到指定值,然后会调用signalAll()将条件队列中的移入到sync queue去执行。 再说说 CountDownLatch ,初始化: 声明一个数字,然后调用自己的await方法,等待其他线程通过调用countDown方法将此数字减到0,然后唤醒当前的线程。 功能 :当前线程等待其他线程执行完之后再执行。 特性:使用CAS和AQS保证线程安全 区别 : 1.cyclicBarrier使用加法到一定数目后唤醒所有线程,countDownLatch使用减法到0时唤醒主线程。 2.cyclicBarrier可以循环使用