多线程性能分析

欢迎光临我的博客[http://poetize.cn],前端使用Vue2,聊天室使用Vue3,后台使用Spring Boot

如果越多的资源被消耗在锁的管理和调度上,那么应用程序得到的资源就越少。
锁的实现方式越好,将需要越少的系统调用和上下文切换,并且在共享内存总线上的内存同步通讯量越少。

线程引入的开销

上下文切换:
	线程调度需要访问由操作系统和JVM共享的数据结构。

	如果新的线程被切换进来,所需要的数据不在当前处理器的本地缓存中,
	上下文切换将导致一些缓存缺失,因而线程在首次调度运行时会更加缓慢。

	频繁的IO操作(阻塞)将增加线程的上下文切换。


内存同步:
	synchronized 和 volatile 提供的可见性保证中可能会使用一些特殊指令,即内存栅栏。
	内存栅栏可以刷新缓存,抑制指令重排。
	内存栅栏抑制编译器的优化,对性能产生间接影响。

非公平锁性能高于公平锁

恢复一个被挂起的线程与该线程真正开始运行存在者严重的延迟。

非公平锁允许插队,可以避免这种延迟。

如果锁的持有时间较长,则应该使用公平锁,因为非公平锁带来的吞吐量提升可能不会出现。

减少锁竞争

1. 减少锁的持有时间:使用同步代码块
2. 降低锁的请求频率:锁分解和锁分段
3. 放弃独占锁:使用并发容器,读写锁,不可变对象,原子变量。


锁分解:
	如果一个锁需要保护多个相互独立的状态变量,那么可以将这个锁分解为多个锁,每个锁只保护一个变量


锁分段:
	劣势:获取多个锁实现独占访问时,获取更加困难并且开销更大。比如 jdk1.7 之前 ConcurrentHashMap 的 size() 操作。

ReentrantLock(显式锁)

轮询锁和定时锁:
	通过 tryLock() 实现(内置锁无法中断一个正在等待获取锁的线程)

可中断的锁获取操作:
	lockInterruptibly() 能够在获取锁的同时保持对中断的响应(定时的 tryLock() 同样能响应中断)

Concurrent性能和可伸缩性优于synchronized的原因

原子变量与非阻塞同步机制


原子变量:
	使用CAS机制。


非阻塞同步机制(CAS):
	一个线程的失败或挂起不会导致其他线程的失败或挂起。
	非阻塞的栈和链表等。


悲观锁(synchronized和ReentrantLock):
	始终获取锁(独占锁),失败则阻塞。
	适合多写的情况(竞争大)。

乐观锁(atomic包):
	不上锁,通过冲突检查机制(CAS)判断在更新过程中是否存在其他线程干扰,操作失败可以重试。
	适用于多读的应用类型(很少发生冲突),这样可以提高吞吐量。

	缺点:
		ABA问题
		循环开销大(不成功一直循环)
		只能保证一个共享变量的原子操作
posted @ 2019-10-26 19:25  LittleDonkey  阅读(721)  评论(0编辑  收藏  举报