zoukankan      html  css  js  c++  java
  • volatile与CAS

    volatile与CAS

    volatile

    作用:(面试常考 必须记住)

    1.保障线程可见性

    线程运行过程如上图所示 先访问 然后吧这个值copy一份到自己的工作空间,然后再去对这个值进行改变,但检查这个方法有无新值不容易控制,加入了volatile在之后保证了线程的可见性 当main主线程对m方法值进行更改之后 t1就会马上看到

    2.禁止指令重排序

    指令重排序是和cpu有关系的 每次写都会被线程读到,加了volatile之后,cpu原来执行一体哦啊指令的时候是一步步执行,但是现在cpu为了提高效率,它会把指令并发的来执行,第一个指令执行得到一半的时候第二个就可能已经开始执行了,这叫流水线式执行。

    这是new一个方法的过程图

    如果没有volatile cpu可能就会对1 2 3 步重排序 可能在杠申请内存给a赋初始值0 的时候就把a调走了

    所以给它加了volatile之后就不会出现指令重排序的可能。

    CAS(无锁优化 自旋)

    假如我原先想改变某一个值0 ,我想把它变成1,但是其中我想做到线程安全,就只能加锁 synchronized,不然线程就不安全。我们现在可以用另一个操作来代替这把锁,就是cas操作,可以把它想象成一个方法,这个方法有三个参数,cas(V,Expected,New Value)。

    V第一个参数就是要改的那个值;Expected第二个参数是期望当前的这个值会是及;NewValue是要设定的新值。比如原来这个值变成3了,我这个线程想改这个值的时候我一定期望你现在是3,是3我才改的,如果你在我改的过程变成了4了,那你跟我的期望值就对不上了,说明有另一个线程改了这个值,那我这个CAS就重新再试一下,再试的时候我希望这个值是4,在修改的时候期望值就是4,没有其他的线程修改这个值,那好,我概念改成5,这就是CAS操作。

    ABA问题(面试)

    假如是你有一个值,我拿到这个值是1,想把它变成2 ,我拿到1用cas操作,期望值是1,准备变成2,这个对象Object。早这个过程种,没有一个线程改过的话我肯定是可以更改的,但是如果有一个线程把这个1变成2后来又变成了1.中间值发生过更改,它不会影响我这个cas下面的操作,这就是ABA问题。

    怎么解决:

    如果是int类型的,最终值是你所期望值,没有关系,这种没有关系可以不去管这个问题;如果确实想管这个问题 可以加版本号,做任何一个值的修改,修改完之后加1,后面检查的时候连带版本号一块都检查。

    总结:如果是基础类型,无所谓,不会影响结果值;

    ​ 如果是引用类型:就像你的女朋友和你分手后又复合,中间经历了其他的男人;(通俗易懂)

    第三节 Atomic类和线程同步新机制

    CountDownLatch

    latch意思是门栓 ,这个锁的意思就是给线程加了给门栓 (倒数的一个门栓5,4,3,2,1,计数完了,门栓打开程序继续往下执行。

    CycicBarrier

    CycicBarrier是一个同步工具 意思是循环栅栏,假如两个线程 一个定义了100个参数线程 在主线程调用的时候我给它加上这么一个栅栏 满20人 栅栏就倒了 到了就把数据放出去 然后栅栏再立起来 以此类推 知道100个参数全出去。就像坐车一样 100个人坐车 但车只有20座位 上来20 线程一看满20了 好 发车 然后再来 再满20 好 发车,这样运行五次就100个人全走了。

    Phaser

    Phaser就是一个分阶段的栅栏 更像是结合了CountDownLatch和CycicBarrier

    ReadWriteLock(重点)

    意思很明显 字面意思 读 写 就是叫读写锁

    读写锁的概念其实就是共享锁和排他锁,读锁就是共享锁,写锁就是排他锁。

    就好比学校的组织结构 有多个人同时访问学校网址这个组织结构 有读的 有写的 加入A同学想往里面写个名字 叫王八摔 但是刚写到王八 数据就被B同学读走了 所以为了避免这种情况我们就要加锁

    这种锁new ReentranReadWriteLocl()是ReadWriteLock的一种实现,在这给实现里我们又分出两把锁来 一个是readLock,另一个是WriteLock,通过它的方法readWriteLock.readLock()来拿到readLok对象,读锁我们就拿到了。通过readWriteLock.writeLock()拿到write对象。这两把锁在我读的时候扔进去,因此,读线程是可以一起读的,也就是说所有的读线程可以一秒完成。

    总结:加了这个读写锁意思就是允许其他读线程一起读 ,写线程来了我不给它写,你先别写,等我读完了你再写。读线程读的时候我们一起读 ,因为读是不改变线程原来的内容,写线程上来就把整个线程全锁定,你先不要读,等我写完你在读。

    Semaphore

    简单含义就是限流

    Semaphore s = new Semaphore() 是来控制几个线程同时执行的 售票处 你不管再多的人来买票 我售票窗口就三个 只能允许三个线程同时运行。

    acquire();这个方法是阻塞方法 acquire的意思就是得到 如果Semaphore s = new Semaphore(5)写的是5 那我acquire一下之后它就得减一变成4 后面的线程此时如果同时执行 访问到的就是四 以此类推 直到变成0 就是五个线程同时运行了 别的线程就等着等到下一批五个 五个 。。。

    线程结束的时候必须要加个 release();方法 因为我刚acquire完 减一 如果线程完成后不加回来 那么后面线程就没办法运行了 ,所以必须要加release 还原化。

    Semaphore s = new Semaphore(1,true)第二个值穿true是设置公平锁,但这基本用不到 因为用到这个就是能控制线程的先来后到 这个不容易控制 所以说这个公平锁意义不大。

    Exchanger

    这个就是扩展知识面用的 简单说就是一个交换器 就是把两个线程里面的数据交换一下

    这个使用场景比较多的应该就是游戏里面双方互换装备 。

  • 相关阅读:
    应用系统之间传输数据的几种方式
    解决
    springmvc httprequest 使用@Autowired注解
    JVM client模式和Server模式的区别
    延时接通电路
    C语言中getch()、getche()和getchar()
    结构体
    五:分布式事务一致性协议paxos的应用场景
    四:分布式事务一致性协议paxos通俗理解
    三:分布式事务一致性协议2pc和3pc
  • 原文地址:https://www.cnblogs.com/beizhai/p/13796272.html
Copyright © 2011-2022 走看看