线程安全的实现方法

什么是线程安全?
多个线程不管以何种方式访问某个类,并且在主调代码中不需要同步,都能表现正确的行为。

线程安全如何实现?
Java中线程安全主要是通过同步互斥非阻塞同步无同步方案这三种手段实现的:

  • 同步互斥悲观并发策略,指多个线程并发访问共享数据时,保证共享数据段在同一时刻只能被一个线程访问。

    • synchronized关键字,synchronized关键字可以给代码块、普通方法和静态方法加锁
    • ReentrantLock(重入锁),相比synchronized增加了一些高级功能:
      • 可实现公平锁,按照申请锁的顺序来依次获取锁;
      • 可绑定多个条件condition,实现精确唤醒;
      • 等待可中断,在等待的线程可以选择放弃等待,改为处理其他事情。

    同步互斥最主要的问题就是线程的唤醒和阻塞带来的性能问题,因为Java的线程映射到操作系统的线程之上,如果要阻塞或者唤醒一个线程,都需要操作系统帮忙完成,从用户态转换到内核态,这种状态转换需要耗费很多处理器的时间。

  • 非阻塞同步乐观并发策略,基本思想是先进行操作,如果没有其他线程争用共享数据,那么操作成功;如果共享数据被争用,那么久采用其他的补偿措施,比如一直不断的重试,直到成功为止。这种操作主要由硬件指令集来实现,在Java中主要有以下几种实现:

    • CAS指令(比较与交换),主要包括三个操作数,分别为内存地址,期望值,旧值。当指令执行时,将内存地址的值和旧值进行比较,如果相同,则将值修改为期望值;否则就不执行。这个过程是一个原子操作。

      CAS可能会出现ABA问题,当一个变量在初次读取时值为A,在准备赋值的过程中,这个值被改为了B,后来又被改为A,那CAS操作就会认为这个没有发生改变。

      ABA问题解决办法:控制变量值的版本来保证,JUC提供了原子引用类来解决这个问题。

      该操作由Unsafe类中的几个方法包装提供,不是提供给用户程序调用的类,因此不能采用反射的手段,只能通过其他的API来间接使用,如整数原子类。

    • 原子类AtomicInteger,调用了Unsafe类的CAS操作,保证自增操作的原子性。

  • 无同步方案:要保证线程安全,不一定就要进行同步。如果一个方法本来就不涉及共享数据,那么自然也不需要同步来保证正确性。

    • 线程本地存储,通过TreadLocal类来实现线程本地变量副本存储。每个Thread对象有一个TreadLocal.TreadLocalMap对象,这个对象保存了以TreadLocal为键,本地线程变量为值的K-V键值对。

参考资料:周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社, 2011.

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值