文章字数:857,阅读全文大约需要3分钟
一、如何实现
- 栈封闭,变量处于方法内部,则此变量处于线程私有区域,是安全的
- 无状态,例如基本的加减乘除,无论多少线程调用都不会有问题
- 类不可变,成员变量位final的基本类型的类,此类只能初始化时赋值,其他时候无法改变。或者成员变量无法获取,只能做判断。(Akka框架)
- volatile,一个线程写,多个读的情况可以实现线程安全。例如线程安全的map,写加锁,读不加。
- 加锁或CAS
- 安全的发布,所谓的发布即将内部的成员变量的引用return,外部可以使用此引用进行对象操作。安全的发布就是返回类的副本,让外部无法直接操作
- ThreadLocal,线程数据隔离
二、Servlet线程不安全
servlet
很少有线程安全的需求- 接受请求,返回都是一个线程负责
- 请求到达生成,请求返回销毁
三、线程不安全问题
死锁,两个及以上线程执行中资源竞争阻塞,如果没其他因素推动,将一直持续下去的情况。(多个资源占用,一个资源不会发生)
怀疑死锁jps -m
查看当前机子上运行的java及进程号jstack id
检测死锁动态顺序死锁,根据参数,锁定的顺序不确定,可能导致的死锁。
首先要将不确定的顺序变成固定的,可以使用
hashCode
将两个锁的排序指定锁顺序。 System.identityHashCode(xx)可以直接调用最底层的hashCode,避免调用对象重写的HashCode
如果hash值一致,则竞争获取一个static的锁,获胜者即可将所有资源获取。使用显式锁tryLock去分别获取锁,获取不到则释放重新尝试
释放后需要休眠随机时间,避免死锁。
- 线程饥饿,低优先级的线程总是拿不到执行时间
- 性能,线程切换需要时间,如果切换的消耗不能抵消节约的时间,就不划算。
- 延迟时间和吞吐量(相应时间和处理任务能力),往往是矛盾的。一般来说,吞吐量更受重视。
- 优先保证程序正确,确实达不到要求时再考虑提供速度
- 一切以测试为基准
- 锁的范围,粒度尽量小。范围指定是代码的区域,粒度指的是如果有多个锁保护的对象,对象直接相互独立,就可以使用多个锁。
- 锁分段,替换独占锁。
四、单例模式线程安全
- 懒汉模式-双重检查可能会有势力内存地址以及引用,但是对象内部值还没分配的情况,可以增加
volita
修饰变量。 - 懒汉模式-延迟占位模式,使用私有内部类类持有变量,获取时用私有类.变量获取,虚拟机保证在获取时才会实例化内部类生成变量。