多线程安全和锁优化概述

什么是线程安全?

通俗的说就是对象本身完成了同步优化,保证外部随意调用的不用在考虑同步,互斥,调配等问题

线程安全的5个等级

1.不可变。
基本类型是用final修饰,对于对象指的是行为不会改变内部状态。比如String类的操作都是返回一个新的String。还有Integer等类,内部变量都是final的。

2.绝对的线程安全

定义是:不管任何条件,调用者都不用做额外的同步措施。

实际是很难达到的。Vecter类,对于内部的add(),remove等方法,都做了一些syc操作。但是也不是绝对的线程安全,比如初始化一个vecter,然后创建两个线程,一个线程做remove,另一个做print。就会出错。

3.相对的线程安全
vecter就算是了,我们经常说的线程安全,就是这个。

4.线程兼容

线程不安全的。但是可以通过外部的措施,保证线程安全。

5.线程对立

无论是否是否采取措施,都无法在多线程中使用。
比如Thread的suspend和resume方法,一个是中断,一个初始化,如果多线程调用就会可能死锁,目前已经逐渐废弃。

线程安全的方法

1.互斥

互斥最常用的方法,同布是目的和结果
synchronized对用monitorenter monitorexit两条字节码指令是系统级别的支持,最常用的。
还有一种API级别的ReentranLock可重入锁。
ReentranLock还有一些syn没有的特性,比如等待中断(等一会,就不等了,不会出现持续阻塞的情况),公平锁(根据时间排队),绑定对象。

互斥实现同步缺点:
频繁的进行线程状态切换,涉及用户态到内核态的切换调度,需要对锁计数,判断是否有线程需要唤醒等

2.非阻塞同步

本质就是把一些非原子操作变为原子操作。
支持的有5种:
测试并设置(test-and-set)
获取并增加(fetch-and-add)
交互(swap)
比较并交换(compare-and-swap)
加载链接/条件存储(Load-link/Store-condition)

最常用的就是由这些这些指令封装的一些类。比如atomicInteger等Atomic系列的类,比如atomicInteger的IncreateAndGet()方法,实现增加,内部就调用了原子操作,compareA你的Set(旧值,新值)

3.无同步方案

线程安全不一定要同步,满足可重入性即可。

可重入代码:
任何时刻转移到其他代码,再回来程序不会出错。本质是无状态改变,任何输入的返回结果可预测。

锁优化

1.自旋锁和自适应自旋

本质就是遇到同步,先不阻塞,成本太高,就让处理器继续自旋状态,需要处理器的支持,缺点是自旋时间太长会影响处理器效率。
自适应自旋就是根据统计性能,自适应自旋的效果。

2.锁消除

本质是把不惜要的锁去掉。
比如字符串相加
String a1,a2,a3.
a1+ a2 +a3;
会变成StringBuilder.append(a1).append(a2).append(a3)
StringBuilder内部方法做了加锁互斥同步,有时候没必要,需要清除。

3.锁粗化

这个很好理解,如果锁太细粒度了,需要频繁的状态切换和调度。比如循环体里面加锁,干脆外部直接加一个大锁。

4.轻量级锁 和偏向锁

这两种锁本质都不是直接加锁。轻量级锁 本质是对于没有线程竞争的,标记为轻量级锁,遇到线程竞争再升级为经常的重量级锁,并在释放以后唤醒相应的线程。

偏向锁是什么也不做,偏向第一个使用对象的线程,当遇到其他线程来用的时候,再升级为重量锁。

发布了586 篇原创文章 · 获赞 1037 · 访问量 186万+

猜你喜欢

转载自blog.csdn.net/u010321471/article/details/100186065
今日推荐