如果越多的资源被消耗在锁的管理和调度上,那么应用程序得到的资源就越少。
锁的实现方式越好,将需要越少的系统调用和上下文切换,并且在共享内存总线上的内存同步通讯量越少。
线程引入的开销
上下文切换:
线程调度需要访问由操作系统和JVM共享的数据结构。
如果新的线程被切换进来,所需要的数据不在当前处理器的本地缓存中,
上下文切换将导致一些缓存缺失,因而线程在首次调度运行时会更加缓慢。
频繁的IO操作(阻塞)将增加线程的上下文切换。
内存同步:
synchronized 和 volatile 提供的可见性保证中可能会使用一些特殊指令,即内存栅栏。
内存栅栏可以刷新缓存,抑制指令重排。
内存栅栏抑制编译器的优化,对性能产生间接影响。
非公平锁性能高于公平锁
恢复一个被挂起的线程与该线程真正开始运行存在者严重的延迟。
非公平锁允许插队,可以避免这种延迟。
如果锁的持有时间较长,则应该使用公平锁,因为非公平锁带来的吞吐量提升可能不会出现。
减少锁竞争
1. 减少锁的持有时间:使用同步代码块
2. 降低锁的请求频率:锁分解和锁分段
3. 放弃独占锁:使用并发容器,读写锁,不可变对象,原子变量。
锁分解:
如果一个锁需要保护多个相互独立的状态变量,那么可以将这个锁分解为多个锁,每个锁只保护一个变量
锁分段:
劣势:获取多个锁实现独占访问时,获取更加困难并且开销更大。比如 jdk1.7 之前 ConcurrentHashMap 的 size() 操作。
ReentrantLock(显式锁)
轮询锁和定时锁:
通过 tryLock() 实现(内置锁无法中断一个正在等待获取锁的线程)
可中断的锁获取操作:
lockInterruptibly() 能够在获取锁的同时保持对中断的响应(定时的 tryLock() 同样能响应中断)
Concurrent性能和可伸缩性优于synchronized的原因
原子变量与非阻塞同步机制
原子变量:
使用CAS机制。
非阻塞同步机制(CAS):
一个线程的失败或挂起不会导致其他线程的失败或挂起。
非阻塞的栈和链表等。
悲观锁(synchronized和ReentrantLock):
始终获取锁(独占锁),失败则阻塞。
适合多写的情况(竞争大)。
乐观锁(atomic包):
不上锁,通过冲突检查机制(CAS)判断在更新过程中是否存在其他线程干扰,操作失败可以重试。
适用于多读的应用类型(很少发生冲突),这样可以提高吞吐量。
缺点:
ABA问题
循环开销大(不成功一直循环)
只能保证一个共享变量的原子操作
原文地址:https://www.cnblogs.com/loveer/p/11745201.html
时间: 2024-10-16 20:04:56