线程安全和锁优化总结

线程安全和锁优化总结

线程安全

各种操作共享的数据分为5类
1.不可变
不可变对象一定是线程安全的
若共享数据是基本类型,只要是final修饰的就可保证是不可变的
把对象中带有状态的变量都声明为final
2.绝对线程安全
大多数线程安全的类都不是绝对线程安全
3.相对线程安全
对一些特定顺序的连续调用,可以需要在调用端使用额外的同步手段保证调用正确性:如Vector HashTable
4.线程兼容
通过在调用端正确的使用同步手段保证对象在并发环境中安全的使用 ArrayList,HashMap
5.线程对立
Thread类的suspend()和resume()

线程安全的实现方法

1.互斥同步
最常见的正确性保障手段
synchronized:会在同步块前后分别形成monitorenter和monitorexit字节码指令,这俩指令都需要一个reference类型参数知名要锁定和解锁的对象
对线程来说是可重入的
若要唤醒或阻塞线程,需要操作系统帮忙,从用户态切换到和心态,需要耗费很多时间。是重量级操作

ReentrantLock:重入锁 高级功能:
1.等待可中断:或有锁的线程长期不释放锁,等待的线程可放弃等待,处理其他事情,适合处理时间长的同步块
2.公平锁:多个线程等待锁时,按申请时间顺序获得锁,默认非公平,可由构造函数指定。
3.绑定多个条件:多次调用newCondition()

synchronized和ReentrantLock 1.6以后,新能基本持平

2.非阻塞同步
互斥同步主要问题是线程阻塞和唤醒带来的性能问题,属于悲观并发策略

先进性操作,若没其他进程争用共享数据,则操作成功,否则补偿措施(不断重试),不需要挂起线程 属于非阻塞同步

一条指令就完成的操作(CAS)
由Unsafe类中compareAndAwapInt()和compareAndSwapLong()(只有Bootstrap ClassLoader加载的类才能调用)包装提供,即时编译出来就是处理器的CAS指令
AtomicInteger.increamentAndGet() 循环调用的compareAndSet()方法的原子性就是使用了Unsafe中的方法

3.无同步方案:
天成就是线程安全的代码
可重入代码:不依赖存储在堆上的数据和公用的系统资源,状态量都是由参传入,不调用非可重入方法
线程本地存储:共享数据代码能否保证在同一线程中执行
如果对象要被某个线程独享:通过ThreadLocal类实现:其中的ThreadLocalMap对象 独一无二的key,本地线程变量为Value



锁优化

1.自旋锁,自适应锁
互斥同步对性能最大的影响是阻塞的实现,需要转入内核态
让请求锁的线程等一会,不放弃处理器时间,执行一个忙循环,自选次数默认10次,可配置 -XX:PreBlockSpin
适合锁占用时间短,否则只会白白消耗处理器资源

自适应锁:自旋时间不固定,由前一次在同一锁上组选时间和锁的拥有者状态决定,若上次自旋刚成功,认为这次自旋也可能再次成功,允许自旋更久
若很少成功获得锁,省略自旋 避免浪费CPU

2.锁消除
即时编译运行时,对代码上要求同步,但被检测到不存在共享数据竞争的锁 消除
StringBuffer.append()只在一个方法内部,sb对象不会逃逸到外部。则消除锁

3.锁粗化
对同一对象连续加锁,则把锁同步范围扩大,
StringBuffer.append()连续执行,则锁扩展到第一次append和最后一次append

4.轻量级锁
本意是再没有多线程竞争前提下,减少传统重量级锁的使用

HotSpot虚拟机对象头分两部分:HashCode,GC年龄等(Mark Word)+指向方法区对象类型数据的指针,若是数组 还有数组长度

代码进入同步块,,此对象没被锁定,标志位(01),在当前栈帧建立一个锁记录,存储锁对象Mark Word拷贝
用CAS操作将对象Mark Word指向锁记录的指针,若成功,则获得锁 标志位改为(00),次对象处于轻量级锁定状态

若两条以上线程争用同一把锁,轻量级锁膨胀微重量级锁,状态位(10)

解锁过程是CAS操作把对象当前的Mark Word和线程中复制的Displaced Mark Word替换

提升性能的依据是 绝大部分锁,在同步周期内不存在竞争

5.偏向锁
目的是消除数据在无竞争情况下的同步原语
在无竞争情况下把同步消除,不做CAS,偏向第一个获得锁的线程,若没有其他线程获得锁,则已经持有锁的线程不需要同步

启用偏向锁:-XX:+UseBiasedLocking
锁对象第一次被线程获取是,标志位设为(01) 偏向模式 使用CAS吧该线程ID记录在Mark Word中
另一个线程尝试获取这个锁,偏向模式就结束

若程序中大多数锁总被多个不同线程访问,则偏向锁就是多余的,禁用偏向锁:-XX:-UseBiasedLocking



偏向锁是在无锁争用的情况下使用的,也就是同步开在当前线程没有执行完之前,没有其它线程会执行该同步块,一旦有了第二个线程的争用,偏向锁就会升级为轻量级锁,如果轻量级锁自旋到达阈值后,没有获取到锁,就会升级为重量级锁;

如果线程争用激烈,那么应该禁用偏向锁。




猜你喜欢

转载自blog.csdn.net/zhuyong7/article/details/80503844
今日推荐