美文网首页
线程安全的问题

线程安全的问题

作者: Okami_ | 来源:发表于2019-03-12 23:13 被阅读0次

    为什么会有线程安全的问题

    • 在Java虚拟机中,每个线程 的操作都在自己的工作内存中,当要操作主内存中的数据的时候,会先将主内存的数据读取到线程工作内存.操作完成之后再写回到主内存.但是线程之间是无法直接通信的.

    处理线程安全的方式

    一.不共享

    • 1.不使用线程共享数据
    • 2.让共享数据不可变
    • 3.栈封闭

    二.共享但是加锁

    • synchornized
      • synchornized是java内置关键字
      • 属于jvm层面的锁
      • 无法判断是否获取锁的状态
      • synchronized会自动释放锁
      • 如果两个线程同时竞争一个锁,获得锁的线程阻塞,没有获取锁的线程会一直等待
      • synchronized锁可重入,不可中断,非公平锁
    • Lock
      • lock是java.util.concurrent包的接口,有多种实现类
      • lock可以判断锁的状态
      • 需要在最后手动执行unlock方法,否则容易造成死锁
      • 可以尝试获取锁,如果获取不到可以立即返回,也可以在一段时间内获取不到立即返回失败
      • 可重入,可中断,可以为公平锁, 也可以为非公平锁
      • 可能会存在活锁的问题
    • volatile(内存栅栏)
      • 禁止指令重排
      • 更新的内容的时候,直接写到主内存,其他线程读取的时候从主线程读取,保证了内存可见性
      • 缺点:volatile无法保证原子性,多个线程同时对变量i做自增操作的话,会存在问题
    • CAS操作
      • CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做
      • Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性
      • 缺点: 如果变量的值在执行cas操作的时候,已经经历了A->B->A的过程,无法识别
    • 写时复制
      • 在并发访问的情景下,当需要修改JAVA中Containers的元素时,不直接修改该容器,而是先复制一份副本,在副本上进行修改。修改完成之后,将指向原来容器的引用指向新的容器(副本容器)。
      • 由于不会修改原始容器,只修改副本容器。因此,可以对原始容器进行并发地读。其次,实现了读操作与写操作的分离,读操作发生在原始容器上,写操作发生在副本容器上
      • 数据一致性问题:读操作的线程可能不会立即读取到新修改的数据,因为修改操作发生在副本上。但最终修改操作会完成并更新容器,因此这是最终一致性

    死锁

    • 死锁是指多个进程在执行过程中,由于竞争资源或者由于彼此通信造成的一种阻塞的现象,若无外力作用,他们经无法推进.此时称系统产生了死锁

    • 程序的表现: 程序无法向前推进

    • 本地查看

      • dump
      • 如果程序监测到java层死锁的话,会有日志Found one Java-level deadlock
    • 远程查看

      • jsp
      • jstack
    # 把所有在运行的java进程打印出来
    jps -v
    # 打印对应进程的dump信息
    jstack [进程号]
    

    相关文章

      网友评论

          本文标题:线程安全的问题

          本文链接:https://www.haomeiwen.com/subject/wycrfqtx.html