一.JAVA下的线程安全分析
下面代码中可能出现的结果是多少?
出现问题的过程指令分析-线程的上下文切换
线程切换导致值被覆盖
++操作不是单纯的原子操作,里面从指令层面来说,他是4个指令完成一次加完之后的复制
提数据,在计算,在复制。
二.应用之互斥
为了避免临界区的竞态条件发生,java提供了多种手段进行规避:
1.阻塞式的解决方案:synchronized,Lock
2.非阻塞式的解决方案:原子变量
三.synchronized对象锁
采用互斥方式让统一时刻之多只有一个线程持有对象锁,其他线程在获取这个对象锁会被阻塞,不用担心线程上下文切换。
synchronized其实是提供一种去对线程进行阻断的一个条件依据!!!
synchronized---本质与等价方案
等价方案1
等价方案2
存在内存泄露的风险!!!
为什么要用一个对象来加锁? 锁到底加的是什么玩意???
四.Mark中的数据对于并发的支持
附加信息
对象头信息
发生了什么???
当遇到synchronized:
1.首先修改锁标志位;
2.向JVM申请一把锁--》new Monitor;
3.mark word填入Monitor对象地址;
4.Monitor实现锁业务。
Monitor对象数据结构
五.Monitor对象过程注意事项
执行同步代码块内容,然后唤醒entryList中其他线程时,此处采取竞争策略,先到不一定先得,所以synchronize锁是非公平。
非公平锁: 在锁可用的时候,一个新到来的线程要占有锁,可以不需要排队,直接获得。
公平锁: 在锁可用的时候,一个新到来的线程要占有锁,需要排队,等待执行。
synchronized缺点:
1.单线程下,Monitor对象有必要?
2.多线程下,如果一个线程30分钟后才并发?
我们现在是嫌弃monitor占用空间大,而且在某些场景下没有必要。
场景:
1.单个线程在运行---》仅仅只要进行标记一下--->直接标记在我们markword上面
2.两个线程在运行---》我的需求,要不就是1用,要不就是2用,我也不要monitor,但是需要有数据的记录, 这个时候我们要想个地方来存两个线程的数据,所以我找了个地方,就是栈帧里面,好处是什么?
3.多个线程在运行(需要monitor的支持)
所谓的锁膨胀,就是在线程慢慢开辟的过程中,他的处理方案的变更!!!
解决方案:
线程分布的不同锁分类:
偏向锁: 只针对于一个线程,单个线程体系下加锁,本质就只有一把,直接应用markword解决识别问题。
轻量级锁:只针对于两个线程,利用栈区结构来存储线程ID不同。
重量级锁:两个以上线程,利用一个全新的结构来存储不同的ID。
锁膨胀
六.锁膨胀内存应用过程
锁膨胀内存变化
七.有没有比synchronized速度更快的方案?
利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。而整个J.U.C都是建立在CAS之上的,因此对于synchronized阻塞算法,J.U.C在性能上有了很大的提升。
八.CAS
CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
CAS机制当中使用了3个基本操作数:
1.内存地址V
2.旧的预期值A
3.要修改的新值B
更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
CAS例子
线程1发现失败
线程1发现失败后,放弃更新,重新提取内存值,重新计算!!!
CAS,一套方案--》做什么事情?---》同步数据---》同步主内存和工作内存的数据!!!
实际上是在逻辑层面运用算法和判定去规避数据异常的方案,这个方案比加锁强吗?
九.AQS
JUC当中 AQS-->工具包--》API----->是基于CAS理论去处理同步问题的实现ABA。
十.ReentrantLock
ReentrantLock小例子
ReentrantLock的优点:
1.java中已经有了内置锁:synchronized,synchronized的特点是使用简单,一切交给JVM去处理,不需要显示释放。
2.从用法上可以看出,与synchronized相比, ReentrantLock就稍微复杂一点。因为必须在finally中进行解锁操作。
3.相比于synchronized,ReentrantLock在功能上更加丰富,它具有***可重入、可中断、可限时、公平锁***等特点。
十一.ReentrantLock(可重入)
可重入
由于ReentrantLock是重入锁,所以可以反复得到相同的一把锁,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放(重入锁)。
十二.ReentrantLock(可中断)
与synchronized不同的是,ReentrantLock对中断是有响应的.synchronized一旦尝试获取锁就会一直等待直到获取到锁。
十三.ReentrantLock(可限时)
1.超时不能获得锁,就返回false,不会永久等待构成死锁。
2.使用lock.tryLock(long timeout, TimeUnit unit)来实现可限时锁,参数为时间和单位。
可限时例子
两个线程来争夺一把锁,获得锁的线程sleep6秒,每个线程都只尝试5秒去获得锁。所以必定有一个线程无法获得锁。无法获得后就直接退出了。
十四.ReentrantLock(公平锁)
1.一般意义上的锁是不公平的,不一定先来的线程能先得到锁,后来的线程就后得到锁。不公平的锁可能会产生饥饿现象。
2.公平锁的意思就是,这个锁能保证线程是先来的先得到锁。虽然公平锁不会产生饥饿现象,但是公平锁的性能会比非公平锁差很多。
公平锁例子