JAVAEE初阶 多线程进阶(二)

多线程进阶相关知识点

    • 一.CAS
      • 1.1 CAS的原子类
      • 1.2 实现自旋锁
      • 1.3CAS中的ABA问题
      • 1.4 ABA问题的解决
    • 二. callable接口
    • 三.reentrantLock
      • 3.1 reentrantLock与synchronized区别
    • 四.信息量 semaphore
    • 五. CountDownLatch
    • 六. concurrentHashMap
      • 6.1 concurrentHashMap的优点

一.CAS

CAS compare and swap 比较并交换 比较交换的是内存和寄存器中的值
就能原子的完成一些复杂操作,达成无锁化编程.

1.1 CAS的原子类

JAVAEE初阶 多线程进阶(二)_第1张图片

1.比较相等
2.如果相等 就将新值赋值给address中
3.返回操作
但上述伪代码并不是原子的

JAVAEE初阶 多线程进阶(二)_第2张图片这些代码是在java.util.concurrent.atomic包中的原子类,是基于CAS实现的.

JAVAEE初阶 多线程进阶(二)_第3张图片

JAVAEE初阶 多线程进阶(二)_第4张图片

1.2 实现自旋锁

JAVAEE初阶 多线程进阶(二)_第5张图片

实现自旋锁,如果this.owner不为空 就一直进行"忙等" 循环会一直执行下去
此处自旋的等 虽然没有放弃cpu 但是不会参与调度, 缺点会消耗很多CPU资源

1.3CAS中的ABA问题

CAS的判定本质上是判断是否有其他的线程从中间穿插进来.
我们先看一个例子

JAVAEE初阶 多线程进阶(二)_第6张图片
极端情况:
JAVAEE初阶 多线程进阶(二)_第7张图片

在t1执行之前 t3线程又给我账户充值了500块钱,我们就分不清楚到底是取了500块钱,还是取了又充值回去的,就会引起一个BUG.

1.4 ABA问题的解决

1.约定数据变化只能是单向的(要增就只能都增)
2.如果必须是双向变化的数据,我们可以引入版本号.版本号的数字就只能是增加或者减少的.

二. callable接口

我们前面学过实现runnable接口来完成线程问题,这里的callable接口有什么不同呢?

  1. runnable接口关注的是过程,而不是结果,所以提供的run方法,返回值为void
  2. callable接口关注的是结果,提供的call方法的返回值就是callable接口的泛型类

JAVAEE初阶 多线程进阶(二)_第8张图片

除此之外,在callable接口中,我们为什么不能直接将callable放到Thread的参数里面呢?因为Thread没有提供相应的构造方法,我们需要借助FutureTask来辅助实现

三.reentrantLock

它是一个可重入锁, 与synchronized类似.
reentrantLock提供了两个方法, lock方法和unlock方法,但是如果unlock有时会无法解锁,这是因为碰到return或者异常之后就会执行不到unlock,所以必须将unlock方法与try-finally方法连用.

3.1 reentrantLock与synchronized区别

区别1 : reentrantLock方法提供了tryLock方法
普通的lock方法进行加锁,如果无法加锁,就会阻塞
tryLock方法加锁不成,不会阻塞,会直接返回false

区别2 : reentrantLock提供了公平锁的实现
所谓公平锁就是要遵循先来后到的原则,通过队列记录加锁线程的先后顺序.

区别3: 搭配的等待通知不一样
synchronized搭配的是 wait-notify方法
reentrantLock搭配的是Condition方法

四.信息量 semaphore

信息量表示 可用资源的个数
申请一个资源,可用资源的个数就会减1 称为P操作
释放一个资源,可用资源的个数就会加1 称为V操作

JAVAEE初阶 多线程进阶(二)_第9张图片

acquire表示申请一个资源 release表示释放一个资源

五. CountDownLatch

它的目的就是 多线程执行一个任务, 把大的任务拆分成几个部分 ,分给每个线程执行.

JAVAEE初阶 多线程进阶(二)_第10张图片

六. concurrentHashMap

我们知道哈希表中有 HashMap , Hashtable 在多线程中,我们知道.HashMap是线程不安全的所以不予考虑. Hashtable在关键方法上都有synchronized加锁,但是我们也不常用, 这就引入了concurrentHashMap

6.1 concurrentHashMap的优点

  1. 缩小了锁的粒度

JAVAEE初阶 多线程进阶(二)_第11张图片

在Hashtable中 如果修改两个不同链表中的元素时,不会有线程安全问题.如果是修改同一个链表上的元素时,就会发生锁冲突,有线程安全问题.

JAVAEE初阶 多线程进阶(二)_第12张图片

在concurrentHashMap中,给每一个链表都发了一把锁,因此就不会发生锁冲突.线程会更加的安全.

优点2 :充分的使用了CAS的原子操作,减少了加锁.

优点3 : 针对扩容问题,增加了优化
我们知道,在哈希表中有负载因子:描述了桶上平均有多少元素.
哈希表的查找效率是O(1) 但是你超过了原有元素的个数之后,我们要进行扩容,把旧的元素全部添加到新的元素上,如果本身有很多元素的话,会非常消耗时间.
HashMap的做法是一次性的全部扩容,就会非常耗费时间
concurrentHashMap的做法是 一次扩容一部分 分多次来扩容 ,避免了一次性的耗费时间太多的问题.避免出现某一时间段卡的情况.

你可能感兴趣的:(java-ee,java)