多线程进阶学习

将围绕下面开始学习:
1.各种锁策略.
2.CAS机制.
3. synchronized原理和优化机制.
4. java.util.concurrent (简称juc)包中的重要组件
5. ConcurrentHashMap

1.锁策略广泛,宏观的不仅仅局限于Java . 各种需要设计锁的场景都可能会涉及到~
以下介绍的锁策略,都是面试中比较常见的:
1)乐观锁vs悲观锁
乐观锁:世界大概率是和平的,多个线程竞争一把锁的概率会很低. [效率更高]
悲观锁:世界大概率是出问题的,多个线程竞争一把锁的概率会很高 会付出更多的成本来进行锁冲突的处理[更安全]
两种想法没有优劣之分,要根据具体场景来进行使用.
2)读写锁
把加锁操作分成了两种.
a)读锁
b)写锁
读锁和读锁之间是没有互斥的(不存在锁竞争)
读锁和写锁/写锁和写锁之间才进行互斥.
如果某个场景下"-写多读"
使用读写锁效率就很高.
3)重量级锁vs轻量级锁.
加锁需要保证原子性.原子性功能来源自硬件. (硬件提供了相关的原子操作的指令,操作系统封装- - 下成为原子操作的接口,应用程序
才能使用这样的操作)
在加锁过程中,如果整个加锁逻辑都是依赖于操作系统内核,此时就是重量级锁. (代码在内核中的执行开销会比较大)
对应的,如果大部分操作都是用户自己完成,少数操作由内核完成,这种就是轻量级锁.
把单子交给银行柜员,柜员就要一顿操作. 具体操作多久,不可控的.相当于内核态执行代码.
4.挂起等待锁vs自旋锁
挂起等待锁表示当获取锁失败之后,对应的线程就要在内核中挂起等待(放弃CPU,进入等待队列),
需要在锁被释放之后由操作系统唤醒. [通常都是 重量级锁]
自旋锁表示在获取锁失败后,不会立刻放弃CPU,而是快速频繁的再次询问锁的持有状态, - -旦锁被释放了,就能立刻获取到锁.
[通常都是轻量级锁]
自旋锁的效率更高,但是会浪费一些CPU资源(自旋过程相当于CPU在空转)
线程能够获取CPU资源是一件来之不易的事情.
-旦线程挂起,下次啥时候能被调度上,就不可预期了. (时间可能很久,能够达到ms
级,极端情况下可能是s级)
Windows/Linux这样的系统,调度过程都是没那么快的~~调度时间不可预期.

5.公平锁vs非公平锁
如果多个线程都在等待一把锁的释放.
当锁释放之后,恰好又来了一个新的线程也要获取锁
公平锁:能保证之前先来的线程优先获取锁.
非公平锁:新来的线程直接获取到锁,之前的线程还得接着等
要想实现公平锁,就需要付出一-些额外的代价
6.可重入锁
-个线程针对一把锁,连续加锁两次,不会死锁,这种就是可重入锁.
涉及到一个重要的概念:死锁~~
如果锁记录自己是被谁持有的,就可以进行特殊判定了.
当锁的持有者正好就是新的锁的申请者,此时就特殊
处理下让加锁操作成功即可.
死锁的典型场景:
1.一个线程针对一把锁连续加两次
2.两个线程针对两把锁分别加锁
3. N个线程针对N把锁分别加锁.

CAS机制

硬件设备提供的一种基础指令,基于这样的基础指令,就可以实现一些特殊功能(实现锁)。

应用场景无锁编程
不使用锁,而是直接用CAS来保证线程安全.

锁优化

1.锁消除:编译器+ JVM会根据代码运行的情况智能判定当前的锁是否必要.如果不必要,就直接把锁的代码干掉.

2.偏向锁:第-个尝试加锁的线程,不会真的加锁而是进入偏向锁状态. (很轻量的标记) ,直到其他线程也来竞争这把锁,才会取消偏向锁状态,真正进行加锁.
3.自旋锁:真的有多个线程竞争锁的时候,偏向锁状态被消除,此时线程使用自旋锁的方式来尝试进行获取锁.
自旋锁能保证让其他想竞争锁的线程尽快获取到锁.付出了一定的CPU资源.
4.锁膨胀(无奈之举,严格上说,不能算优化)
当锁竞争更加激烈,此时就会从自旋锁状态膨胀成重量级锁(挂起等待锁)
5.锁粗化
理解锁的"粒度’
直观的认为加锁状态下要执行的代码
越多,锁粒度就越粗,反之就越细.
在这里插入图片描述

JUC

1.原子类
2.locks
3. Callable / Future / FutureTask
创建线程的方式:
a.直接创建类,继承Thread,重写run方法. (具名类/匿名类)
b.创建Runnable接口,重写run方法,设置到Thread中(具名类/匿名类)
c.使用lambda表达式.
d.使用Callable搭配Future Task也能创建线程.
4. Executors ExecutorService ThreadPoolExecutor
线程池.
避免频繁创建和销毁线程.
5. Semaphore信号量.
停车场,门口会有一一个电子显示牌. xx个空闲车位. =>信号量.
有车开进去,车位数- 1,有车开出来,车位数+ 1
信号量本质上就是一个计数器(整数),表示可用资源的个数.
申请使用资源,计数器- 1§, 释放资源,计数器+ 1. (V)

ConcurrentHashMap

线程安全的集合类.
重点是ConcurrentHashMap
前面学过的集合类,大部分都是线程不安全的.
Vector, Stack, HashTable,线程安全的.其他的都不安全.
ArrayList / HashMap就是一个线程不安全的.
使用线程安全的版本进行代替.
对于ArrayList来说:
1.自己加锁.
2. Collections .sychronizedList
对于HashMap来说
1.自己加锁
2. HashTable
3. ConcurrentHashMap

谈谈HashMap, HashTable, ConcurrentHashMap之间的区别.
HashMap线程不安全.
HashTable和ConcurrentHashMap线程安全.
HashTable简单粗暴的使用sychronized来进行加锁.
一把大锁控制所有. .
一个HashTable实例内部只有一把锁.
这就意味着只要针对这个实例进行操作,都需要先申请锁,冲突概率就比较大~~
效率比较低.
ConcurrentHashMap内部有多把锁(每个桶有一把锁)大大降低了冲突概率~~ 提高了效率.

HashTable的扩容就是一鼓作气. 某个操作插入元素后触发扩容,就得由该线程把整个hash表都完成扩容后才能算
是插入完毕. (耗时非常久)
ConcurrentHashMap的思路"大事化小”,当需要扩容的时候,不会由某-次操作彻底完成扩 容,而是由后续的- -系
列操作都要进行扩容. (每次扩容一点点)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lhj_loveFang_1105

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值