多线程性能分析

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

线程引入的开销

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

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

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


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

非公平锁性能高于公平锁

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

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

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

减少锁竞争

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


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


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

ReentrantLock(显式锁)

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

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

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

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


原子变量:
    使用CAS机制。


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


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

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

    缺点:
        ABA问题
        循环开销大(不成功一直循环)
        只能保证一个共享变量的原子操作

猜你喜欢

转载自www.cnblogs.com/loveer/p/11745201.html
今日推荐