多线程与高并发
- Volatile
Volatiles是java虚拟机提供的轻量级的同步机制
三大特性:保证可见性,不保证原子性,禁止指令重排
- CAS
CAS(CompareAndSet) 比较并交换算法,保证数据原子性
UnSafe类是其一个实现的类
缺点:循环时间长,开销大。只能保证一个共享变量的原子操作。
AtomicInteger是一个支持原子操作的Integer类,它提供了原子自增方法、原子自减方法以及原子赋值方法等。其底层是通过volatile和CAS实现的,其中volatile保证了内存可见性,CAS算法保证了原子性。
- ABA
问题描述:某时刻从内存中取出数据比较并交换,这个时间差会导致数据变化,尽管线程A的CAS操作成功,但是不代表这个过程没有问题
解决方法:原子引用+新增一种机制,修改版本号
atomicReference -> automicStampedReference
- 不安全集合类
故障现象:java.util.ConcurrentModificationException 并发修改异常
导致原因:并发争抢修改导致
ArrayList解决方案:
-
- 使用线程安全的集合类vector
- 包装成安全的集合类Collections.synchronizedList(new ArrayList<>())
- new CopyOnWriteArrayList<>() 读写分离,源码使用Lock
hashmap解决方案:
- 使用线程安全集合类 ConCurrentHashMap
- 包装成安全的集合类Collections.synchronizedMap(new HashMap<>())
HashSet底层就是HashMap,只是value是一个常量
- 公平锁和非公平锁
公平锁:先来后到,吞吐量大
非公平锁(ReentrantLock默认,Synchronized):允许加塞,有可能造成优先级反转或者饥饿现象
- 可重入锁(递归锁)
线程可以进入任何一个它已经拥有的锁同步着的代码块
例子:reentrantLock和synchronize
作用:防止死锁
- 自旋锁
尝试获取所得线程不会立即阻塞,而是采用循环的方式去获取锁
例子:Unsafe.getAndAddInt
优缺点:优点减少线程上下文切换的消耗,缺点循环会消耗CPU
- 独占锁(写锁)/共享锁(读锁)/互斥锁
独占锁:一次只能被一个线程所持有的(reentrantLock和synchronize)
共享锁:可被多个线程锁所持有(reentrantReadWriteLock读操作)
读读 不互斥
读写 互斥
谢谢 互斥
- CountDownLatch/ CyclicBarrier/ Semaphore
CountDownLatch:其他操作完成后,再执行当前操作,做减法
CyclicBarrier:先到等待,集齐执行操作,做加法
Semaphore:多个共享资源互斥使用,并发线程数控制
- 阻塞队列
阻塞队列为空,取元素被阻塞
阻塞队列为慢,加数据被阻塞
好处:不需要关注什么时候阻塞线程,什么时候唤醒线程,阻塞队列完成
接口:BlockingQueue,和List平级
实现类:
- 生产消费者模型
多线程判断使用while,不能使用if
- Synchronize和Lock区别
- 原始构成:synchronize是关键字术语jvm层面,lock是具体类是api层面
- 使用方法:synchronize不需要手动释放,reentrantlock需要手动释放
- 等待是否可中断:synchronize不可中断,reentrantlock可以中断
- 加锁是否公平:synchronize非公平锁,reentrantlock默认非公平,可以公平
- 绑定多个条件condition:synchronize没有,reentrantlock可以分组唤醒,可以精确唤醒
- 线程池
线程池的特点和优点
三个常见线程池的使用(底层是ThreadPoolExecutor)
实际应用中都不使用,并发访问量巨大时会出现oom,因此需要手写线程池
线程池的七个参数
线程池原理
- 死锁
两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象
查看死锁:
- jps –l 定位进程号
- jstack 9636 找到死锁查看