
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
锁的应用是许多Java编程开发程序员都需要熟练掌握的一个计算机编程知识,而本文我们就通过案例分析来简单了解一下,Java编程锁的应用与类型分析。
1、Lock接口
Lock接口的实现基本都是通过聚合了一个同步器(AQS)的子类来完成线程访问控制的。
子类推荐被定义为自定义同步组件的静态内部类,例如常用的ReentrantLock中的公平锁和非公平锁。
2、队列同步器
同步器提供的模板方法基本分为三类:
独占式获取与释放同步状态、共享式获取锁与释放同步状态和查询同步队列中的等待线程情况。
同步器是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。
可以这样理解二者之间的关系:
锁是面向使用者的,它定义了使用者与锁交互的接口,隐藏了实现细节;
同步器面向的是锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。
锁和同步器很好的隔离了使用者和实现者所需关注的领域。
3、锁的内存语义
当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。
当线程获取锁时,JMM会把该线程对应的本地内存置为无效,从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。
以ReentrantLock为例,它的实现依赖于Java同步框架AbstractQueuedSynchronizer(简称AQS)。
AQS使用一个整型的volatile变量(命名为state)来维护同步状态,这个volatile变量是ReentrantLock内存语义实现的关键。
ReentantLock分为公平锁和非公平锁,另外ReentrantLock是可重入锁。
这里仅对概念讲解一下:
公平锁:谁等待的时间长谁优先获得锁,也就是符合FIFO原则。
非公平锁:谁先抢到锁,锁就是谁的。通常来说,刚释放锁的线程更有机会再次获得锁。
可重入锁:任意线程在获取到锁之后(没有释放锁)能够再次获取该锁而不会被锁所阻塞,俄罗斯套娃。
4、final域重排序
对于final域,编译器和处理器要遵守两个重排序规则。(具体示例可以参阅书籍)
在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。
Ⅰ、写final域的重排序规则
写final域的重排序规则禁止把final域的写重排序到构造函数之外。这个规则包含以下两个方面:
JMM禁止编译器把final域的写重排序到构造函数之外。
编译器会在final域的写之后,构造函数return之前,插入一个StoreStore屏障,从而禁止处理器把final域的写重排序到构造函数之外。
Ⅱ、读final的重排序规则
读final域的重排序规则是,在一个线程中,初次读对象引用与初次读该对象包含的final域,JMM禁止处理器重排序这两个操作。
(注意这个规则仅针对处理器),编译器会在读final域操作的前面插入插入一个LoadLoad屏障。
因为初次读对象引用与初次读该对象包含的final域,这两个操作之间存在间接依赖关系。
由于编译器遵守间接依赖关系,因此编译器不会重排序这两个操作。大多数处理器也会遵守间接依赖,该规则就是针对少数处理器的。
【免责声明】:本内容转载于网络,转载目的在于传递信息。文章内容为作者个人意见,本平台对文中陈述、观点保持中立,不对所包含内容的准确性、可靠性与完整性提供形式地保证。请读者仅作参考。更多内容请加danei456学习了解。欢迎关注“达内在线”参与分销,赚更多好礼。