
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
我们在前几期的文章中给大家简单介绍了程序员在学习Java编程语言的时候需要注意Java线程安全方面的问题,今天我们就再来学习一下,Java线程安全的实现方法都有哪些。
①互斥同步
互斥同步(MutexExclusion&Synchronization)是一种常见的并发正确性保障手段。
同步:多个线程并发访问共享数据,保证共享数据同一时刻只被一个(或者一些,使用信号量)线程使用。
互斥:互斥是实现同步的一种手段,主要的互斥实现方式:临界区(CriticalSection)、互斥量(Mutex)、信号量(Semaphore)。
同步与互斥的关系:
互斥是原因,同步是结果。
同步是目的,互斥是方法。
Java中,基本的实现互斥同步的手段是synchronized关键字,其次是JUC包中的ReentrantLock。
关于synchronized关键字:
编译后的同步块,开始处会添加monitorenter指令,结束处或异常处会添加monitorexit指令。
monitorenter和monitorexit指令中都包含一个引用类型的参数,分别指向加锁或解锁的对象。如果是同步代码块,则为synchronized括号中明确指定的对象;如果为普通方法,则为当前实例对象;如果为静态方法,则为类对应的class对象。
JVM执行monitorenter指令时,要先尝试获取锁:如果对象没被锁定或者当前线程已经拥有该对象的锁,则锁计数器加1;否则获取锁失败,进入阻塞状态,等待持有锁的线程释放锁。
JVM执行monitorexit指令时,锁计数器减1,直到计数器的值为0,锁被释放。(synchronized是支持重进入的)
由于阻塞或者唤醒线程都需要从用户态(UserMode)切换到核心态(KernelMode),有时锁只会被持有很短的时间,没有必要进行状态转换。可以让线程在阻塞之前先自旋等待一段时间,超时未获取到锁才进入阻塞状态,这样可以避免频繁的切入到核心态。其实,就是后面自旋锁的思想。
关于ReentrantLock:
与synchronized关键字相比,它是API层面的互斥锁(lock()、unlock()、try...finally)。
与synchronized关键字相比,具有可中断、支持公平与非公平性、可绑定多个Condition对象的高级功能。
由于synchronized关键字被优化,二者的性能差异并不是很大,如果不是想使用ReentrantLock的高级功能,优先考虑使用synchronized关键字。
②非阻塞同步
(1)CAS概述
互斥同步大的性能问题是线程的阻塞和唤醒,因此又叫阻塞同步。
互斥同步采用悲观并发策略:
多线程并发访问共享数据时,总是认为只要不加正确的同步措施,肯定会出现问题。
无论共享数据是否存在竞争,都会执行加锁、用户态和心态的切换、维护锁计数器、检查是否有被阻塞的线程需要唤醒等操作。
随着硬件指令集的发展,我们可以采用基于冲突检测的乐观并发策略:
先进行操作,如果不存在冲突(即没有其他线程争用共享数据),则操作成功。
如果有其他线程争用共享数据,产生了冲突,使用其他的补偿措施。
常见的补偿措施:不断尝试,直到成功为止,比如循环的CAS操作。
乐观并发策略的许多实现都不需要将线程阻塞,这种同步操作叫做非阻塞同步。
非阻塞同步依靠的硬件指令集:前三条是比较久远的指令,后两条是现代处理器新增的。
测试和设置(TestandSet)
获取并增加(FetchandIncrement)
交换(Swap)
比较并交换(CompareandSwap,即CAS)
加载链接/条件存储(LoadLinked/StoreConditional,即LL/SC)
什么是CAS?
CAS,即CompareandSwap,需要借助处理器的cmpxchg指令完成。
CAS指令需要三个操作数:内存位置V(Java中可以简单的理解为变量的内存地址)、旧的期待值A、新值B。
CAS指令执行时,当且仅当V符合旧的预期值A,处理器才用新值B更新V的值;否则,不执行更新。
不管是否更新V的值,都返回V的旧值,整个处理过程是一个原子操作。
原子操作:所谓的原子操作是指一个或一系列不可被中断的操作。
Java中的CAS操作:
Java中的CAS操作由sun.misc.Unsafe中的compareAndSwapInt()、compareAndSwapLong()等几个方法包装提供。实际无法调用这些方法,需要采用反射机制才能使用。
在实际的开发过程中,一般通过其他的JavaAPI调用它们,如JUC包原子类中的compareAndSet(expect,update)、getAndIncrement()等方法。这些方法内部都使用了Unsafe类的CAS操作。
Unsafe类的CAS操作,通过JVM的即时编译器编译后,是一条与平台相关的CAS指令。
除了偏向锁,Java中其他锁的实现方式都是用了循环的CAS操作。
(2)通过循环的CAS实现原子操作
通过++i或者i++可以实现计数器的自增,在多线程环境下,这样使用是非线程安全的。
【免责声明】:本内容转载于网络,转载目的在于传递信息。文章内容为作者个人意见,本平台对文中陈述、观点保持中立,不对所包含内容的准确性、可靠性与完整性提供形式地保证。请读者仅作参考。更多内容请加danei456学习了解。欢迎关注“达内在线”参与分销,赚更多好礼。