并发安全
类的线程安全定义
如果多线程下使用这个类,不过多线程如何使用和调度这个类,这个类总是表示出正确的行为,这个类就是线程安全的。
类的线程安全表现为:
操作的原子性
内存的可见性
不做正确的同步,在多个线程之间共享状态的时候,就会出现线程不安全。
怎么才能做到类的线程安全?
栈封闭
所有的变量都是在方法内部声明的,这些变量都处于栈封闭状态。无状态
没有任何成员变量的类,就叫无状态的类让类不可变
让状态不可变,两种方式: 1,加final关键字,对于一个类,所有的成员变量应该是私有的,同样的只要有可能,所有的成员变量应该加上final关键字,但是加上final,要注意如果成员变量又是一个对象时,这个对象所对应的类也要是不可变,才能保证整个类是不可变的。 2、根本就不提供任何可供修改成员变量的地方,同时成员变量也不作为方法的返回值
volatile
保证类的可见性,最适合一个线程写,多个线程读的情景,加锁和CAS
安全的发布
类中持有的成员变量,特别是对象的引用,如果这个成员对象不是线程安全的,通过get等方法发布出去,会造成这个成员对象本身持有的数据在多线程下不正确的修改,从而造成整个类线程不安全的问题。TheadLocal
安全问题
死锁
资源一定是多于1个,同时小于等于竞争的线程数,资源只有一个,只会产生激烈的竞争。 死锁的根本成因:获取锁的顺序不一致导致。 怀疑发送死锁: 通过jps 查询应用的 id, 再通过jstack id 查看应用的锁的持有情况 解决办法:保证加锁的顺序性活锁
尝试拿锁的机制中,发生多个线程之间互相谦让,不断发生拿锁,释放锁的过程。 解决办法:每个线程休眠随机数,错开拿锁的时间线程饥饿
低优先级的线程,总是拿不到执行时间
性能思考
1、 先保证程序正确,确实达不到要求的时候,再提高速度。(黄金原则)
2、 一定要以测试为基准。
一个应用程序里,串行的部分是永远都有的。
影响性能的因素
上下文切换
是指CPU从一个进程或线程切换到另一个进程或线程。一次上下文切换花费5000~10000个时钟周期,几微秒。内存同步
- 阻塞
解决
减少锁的竞争
减少锁的粒度
使用锁的时候,锁所保护的对象是多个,当这些多个对象其实是独立变化的时候,不如用多个锁来一一保护这些对象。但是如果有同时要持有多个锁的业务方法,要注意避免发生死锁缩小锁的范围
对锁的持有实现快进快出,尽量缩短持由锁的的时间。将一些与锁无关的代码移出锁的范围,特别是一些耗时,可能阻塞的操作避免多余的缩减锁的范围
两次加锁之间的语句非常简单,导致加锁的时间比执行这些语句还长,这个时候应该进行锁粗化—扩大锁的范围。锁分段
替换独占锁替换独占锁