CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛

「Java基础」「多线程」线程安全与锁实现-永久免费的源码丞旭猿

根据Brian Gortz定义的线程安全是:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。

在代码上的要求是,代码本身封装了所有必要的正确性保障手段,令调用者无需关心多线程的问题,更无需自己采取任何措施来保证多线程的正确调用。

Java代码可以实现线程安全,JVM也提供了同步和锁机制。

多线程问题

在多线程并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题。

  • 原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
  • 可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
  • 有序性:即程序执行的顺序按照代码的先后顺序执行。

Java的线程安全程度

按照安全程度由强至弱排序,可以分为5类:不可变、绝对线程安全、相对线程安全、线程兼容、线程对立。

  • 不可变,基本数据类型加final修饰;对象的行为不会对本身的状态产生任何影响,就保障数据不可变。不可变的对象一定是线程安全的,是最简单的实现。
  • 绝对线程安全,调用者不需要任何额外的同步措施,访问对象的方法都被synchronized修饰为同步方法。但也并非使用时完全不考虑同步,同时效率较低。
  • 相对线程安全,保证对这个对象单独的操作是线程安全的,但是特殊情况下也需要加额外的手段保证线程安全。java.util.concurrent包下都是支持此安全程度的API。
  • 线程兼容,对象不是线程安全的,需要调用端使用同步手段来实现线程安全。
  • 线程对立,无法支持线程安全,Java语言基本上不存在这类情况。

互斥同步实现线程安全

互斥同步(Mutual Exclusion & Synchronization),就是在多个线程并发访问共享数据时,使用互斥来保证共享数据在同一个时刻只被一个或一些线程使用。

Java中有两个最主要方法:synchronized、ReentrantLock。

临界区(Critical Section)、互斥量(Mutex)和信号量(Semaphore)都是主要的互斥实现方式。

  • synchronized

Java中最基本的互斥同步手段是synchronized关键字,该关键字经过编译后,会在同步块前后分别形成monitorenter和monitorexit这两个字节码指令。monitorenter和monitorexit都需要一个reference类型的参数来指明要锁定和解锁的对象。

执行monitorenter时,首先要尝试获取对象的锁。成功获取对象的锁,则把锁的计数器加1。获取失败则当前线程阻塞等待,直到对象锁被释放。

执行monitorexit时就就对象的锁减1,当计数器为0时,锁就被释放。

synchronized是映射到操作系统的原生线程完成,是较重的操作,在用户态转换到核心态时,需要耗费大量时间。后期的JDK对此进行了优化,加入自旋等待策略。

synchronized可以直接指定reference参数。

如果synchronized修饰的是实例方法,reference参数就是this。

如果修饰的是类方法,reference就是此类的class对象。

  • ReentrantLock

除了synchronized之外,还可以用java.util.concurrent包中的重入锁(ReentrantLock)来实现同步。是API层面的互斥锁,用lock unlock 配合try finally完成。加入了等待可中断、公平锁、锁绑定多个条件等高级功能,但并非性能更强。

非阻塞同步实现线程安全

非阻塞同步(Non-Blocking Synchronization),基于冲突检测的乐观并发策略实现线程安全:先进行操作,如果没有其他线程争用共享数据,那操作成功;如果有竞争,产生了冲突,那就再采取其他的补偿措施。

这是性能更好的实现方式,但是这个需要硬件指令集支持:

  • 测试并设置(Test and Set)
  • 获取并增加(Fetch and Increment)
  • 交换(Swap)
  • 比较并交换(Compare and Swap,CAS)
  • 加载链接、条件存储(Load Linked、Store Conditional,LL、SC)

其中CAS由sun.misc.Unsafe类的compareAndSwapInt和compareAndSwapLong等几个方法包装提供,由JVM特殊处理。java.util.concurrent包的整数原子类就是包装了Unsafe类的CAS。

CAS需要有3个操作数:内存位置(变量的内存地址V)、旧预期值(A)、新值(B)。CAS执行时,当且仅当V符合A时,用B更新V,否则不进行更新,但无论是否更新V,都会返回V的旧值,上述处理过程是原子操作。

代码实现线程安全

让一个方法不涉及共享数据,那它自然就无需任何同步措施。这里有两类是天生线程安全的,可重入代码和线程本地存储。

  • 可重入代码(Reentrant Code),可以任意环境执行一段代码并保证返回结果是可以预测的。在代码中不依赖存储在堆上的数据和公用的系统资源、状态量由参数传入、不调用非可重入代码。
  • 线程本地存储(Thread Local Storage),当出现有与其他代码共享数据需求的,如果能保证在同一线程中执行,则可以将数据放在线程的本地存储java.lang.ThreadLocal中,这个对象只有当前线程可用。如果是多个线程访问此数据,那么可以使用volatile声明为易变的。

声明:本文部分素材转载自互联网,如有侵权立即删除 。

© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏 分享
相关推荐
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容