Android JUC02 --- 锁

79 阅读5分钟

一.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.公平锁的意思就是,这个锁能保证线程是先来的先得到锁。虽然公平锁不会产生饥饿现象,但是公平锁的性能会比非公平锁差很多。

                                                              公平锁例子