0%

线程安全

文章字数:857,阅读全文大约需要3分钟

一、如何实现

  • 栈封闭,变量处于方法内部,则此变量处于线程私有区域,是安全的
  • 无状态,例如基本的加减乘除,无论多少线程调用都不会有问题
  • 类不可变,成员变量位final的基本类型的类,此类只能初始化时赋值,其他时候无法改变。或者成员变量无法获取,只能做判断。(Akka框架)
  • volatile,一个线程写,多个读的情况可以实现线程安全。例如线程安全的map,写加锁,读不加。
  • 加锁或CAS
  • 安全的发布,所谓的发布即将内部的成员变量的引用return,外部可以使用此引用进行对象操作。安全的发布就是返回类的副本,让外部无法直接操作
  • ThreadLocal,线程数据隔离

二、Servlet线程不安全

  • servlet很少有线程安全的需求
  • 接受请求,返回都是一个线程负责
  • 请求到达生成,请求返回销毁

三、线程不安全问题

  • 死锁,两个及以上线程执行中资源竞争阻塞,如果没其他因素推动,将一直持续下去的情况。(多个资源占用,一个资源不会发生)
    怀疑死锁
    jps -m查看当前机子上运行的java及进程号
    jstack id检测死锁

  • 动态顺序死锁,根据参数,锁定的顺序不确定,可能导致的死锁。

  1. 首先要将不确定的顺序变成固定的,可以使用hashCode将两个锁的排序指定锁顺序。 System.identityHashCode(xx)可以直接调用最底层的hashCode,避免调用对象重写的HashCode
    如果hash值一致,则竞争获取一个static的锁,获胜者即可将所有资源获取。

  2. 使用显式锁tryLock去分别获取锁,获取不到则释放重新尝试
    释放后需要休眠随机时间,避免死锁。

  • 线程饥饿,低优先级的线程总是拿不到执行时间
  • 性能,线程切换需要时间,如果切换的消耗不能抵消节约的时间,就不划算。
  1. 延迟时间和吞吐量(相应时间和处理任务能力),往往是矛盾的。一般来说,吞吐量更受重视。
  2. 优先保证程序正确,确实达不到要求时再考虑提供速度
  3. 一切以测试为基准
  4. 锁的范围,粒度尽量小。范围指定是代码的区域,粒度指的是如果有多个锁保护的对象,对象直接相互独立,就可以使用多个锁。
  5. 锁分段,替换独占锁。

四、单例模式线程安全

  1. 懒汉模式-双重检查可能会有势力内存地址以及引用,但是对象内部值还没分配的情况,可以增加volita修饰变量。
  2. 懒汉模式-延迟占位模式,使用私有内部类类持有变量,获取时用私有类.变量获取,虚拟机保证在获取时才会实例化内部类生成变量。