1.1.2 线程安全问题

目录

1.2.1 线程安全之可见性(Java内存模型)

1.2.2 线程安全之原子性

1.2.3 JAVA锁相关


1.2.1 线程安全之可见性(Java内存模型)

1、只有共享变量、并且是多线程,至少有一个线程是写操作,才会出现可见性问题

2、jmm:java内存模型

3、局部变量:方法内定义的变量

     成员变量:类的字段(属性)

4、java内存模型制定规则的,JVM是负责实现规则的

Java内存模型:用来描述java语言特性的
5、java内存模型【java语言规范】:是约束、规范java虚拟机
6、JVM运行时数据区【java虚拟机规范】:用来描述java虚拟机
   java内存模型(JMM):用来描述java语言特性只描述线程间操作,不描述线程内操作
7、 脚本语言(解释执行):一条条翻译成机器可识别的指令
    编译语言(编译执行):批量翻译成机器可识别的指令
8、java默认是解释执行,当方法体被频繁调用,或方法体多次循环,就升级成JIT编译执行
9、JIT:just in time,热点代码,即时编译执行
10、volatile关键字:一、禁止缓存
                                二、对volatile变量相关的指令不做重排序

11、synchronized:也可以解决线程可见性问题(解锁前:将共享变量值写入主存;加锁前:重新从主存中读取共享变量值)
12、java内存模型规定:
    对volatile变量v的写入,与所有其他线程后续对v的读同步

1.2.2 线程安全之原子性

1、原子性特征:资源在该次操作中保持一致
2、ReentrantLock:公平锁
3、volatile:先写,然后读,如果没读到,就是可见性问题
atomic原子
4、保证原子性的方法
    synchronized
    Lock
    AtomicInteger
    Unsafe
5、CAS(compare and swap):
    举例说明CAS(1,2):先比较内存中的值是否等于1,如果等于,就将2写入内存,否则取出内存中的值,自旋
    上例中1就是执行方法前从内存中取的值(旧值),2就是执行方法后,需要往内存写的值(新值)
6、硬件保证,同一块内存,同一时间只能有一个线程能够修改
7、J.U.C =java util concurrent
8、AtomicIntegerArray:普通数组,只是set方法用unsafe.put(数组,偏移量,newValue)实现cas操作
9、AtomicIntegerFieldUpdater:原子更新整型的字段的更新器【属性必须用volatile修饰】
10、AtomicReference:原子更新引用类型(只是修改一个引用)
    引用数据类型就用AtomicReference<V>
11、AtomicStampedReference:原子更新带有版本号的引用类型(解决了ABA问题)
12、AtomicMarkableReferenc:原子更新带有标记位的引用类型
13、1.8更新(计数器增强版,高并发下性能更好)
    原理:分成多个操作单元,不同线程更新不同的单元,只有需要汇总的时候才计算所有单元的操作
    场景:高并发频繁更新、不太频繁读的读取
        更新器:DoubleAccumulator、LongAccumulator
        计数器:DoubleAdder、LongAdder
14、CAS的三个问题
    循环+cas,如果操作长时间不成功,会带来很大的cpu资源消耗
    cas只针对单个变量的操作,不能用于多个变量来实现原子操作
    ABA问题(了解就可以了)

15、当并发较高时,LongAdder的效率比AtomicLong好很多,LongAccumulatorLongAdder的功能增强版,LongAdder只有对数值的加减,LongAccumulator可以自定义函数

LongAccumulator accumulator = new LongAccumulator(
                ("参数1","参数2")->{
                    //自己的计算(或比较......)业务逻辑代码
                    return  "想要返回的值";
                },
                "初始值");

        for (int i = 0; i < 3; i++) {//调用自定义函数3次
            accumulator.accumulate(1);
        }

        System.out.println("result=" + accumulator.get());//打印最终结果

1.2.3 JAVA锁相关

1、自旋锁:当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,
           然后不断的判断是否能够被成功获取,直到获取到锁才会退出循环

2、乐观锁:读取时没有加锁,修改时才加锁
     悲观锁:读取时就开始加锁,直到当前线程对这个数据的所有操作结束,才解锁
3、独享锁(写):【互斥】给资源加上写锁,线程可以修改资源,其他线程不能再加锁(读、写锁都不能加),单写(比如:停车位)
   共享锁(读):给资源加上读锁后只能读不能改,其他线程也只能加读锁,不能加写锁;(多读)(比如:令牌、信号量)
4、一般来说独享锁用于写,共享锁用于读
5、可重入锁:一个线程lock拿到锁,还没有unlock之前,再次lock,如果可以拿到锁,就是可重入锁
6、ReentrantLock、synchronized都是可重入锁,大多数时候都用可重入锁
7、公平锁、非公平锁:争抢锁的顺序,如果按照先来后到,则为公平锁
8、几个重要的锁实现方式:synchronized、ReentrantLock、ReentranReadWriteLock
9、synchronized
                互斥(独享锁)、可重入锁、悲观锁
                用于实例方法、静态方法时,隐式指定锁对象
                用于代码块时,显示指定锁对象
                锁的作用域:对象锁、类锁、分布式锁
                锁优化:锁消除(开启锁消除的参数:-XX:DoescapeAnalysis -XX:EliminateLocks)
                              锁粗化
锁消除:没有意义的加锁、解锁,执行很多次,就会JIT消除锁(局部变量、只有一个线程)
锁粗化:锁合并(无意义的加锁、解锁,就会触发JIT),线程数多个、单个都可以
10、偏向锁:在JDK6以后,对象默认开启偏向锁,可通过JVM参数-XX:-UseBiasedLocking来禁用偏向锁,若偏向锁开启,只有一个线程抢锁,可获取到偏向锁
        偏向锁意义:加锁之后就不解锁(相对情况下不解锁,出现争抢就锁升级)
        偏向锁场景:单个线程
11、java对象在内存中的布局:
        对象头
            Mark Word:本质是内存区域,长度根据系统位数而定(32位系统中markword的长度只有32位,64位系统中markword的长度就是64位)
            Class meta address:指针,指向类,标识当前对象的属于哪个类
            Array Length:数组长度(如果当前对象是数组,表示数组的长度)
        属性

MarkWord在64位JVM

MarkWord在32位JVM

12、重量级锁:JVM采用Object Monitor实现
            Owner(锁持有者)
            EntryList(锁池,没有获得锁,等待抢锁):线程的状态是Blocked
            WaitSet(调用了wait方法的线程,等待被唤醒):线程的状态是Waiting
    EntryList、WaitSet都是队列


13、每个对象都有一个Object Monitor,Object Monitor就是一个对象监视器(管程)
14、默认情况下JVM锁会经历:未锁定>偏向锁>轻量级锁>重量级锁 ,这四个状态
15、synchronized没有锁降级,只有解锁,只会解锁到(未锁定,关闭偏向锁)状态,不会到(未锁定,开启偏向锁)状态,如果重量级锁解锁后,下次加锁,直接就是重量级锁,不会经历轻量级锁

16、锁升级的过程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值