- 浏览: 514748 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (563)
- 工作经验 (12)
- 数据库 (13)
- Servlet (10)
- Struts2 (1)
- Spring (25)
- Eclipse (5)
- Hibernate (5)
- Eclips (8)
- HTTP (7)
- J2EE (21)
- EHcache (1)
- HTML (11)
- 工具插件使用 (20)
- JPA (2)
- 杂谈 (17)
- 数据结构与算法 (3)
- Cloud Foundry (1)
- 安全 (10)
- J2SE (57)
- SQL (9)
- DB2 (6)
- 操作系统 (2)
- 设计模式 (1)
- 版本代码管理工具 (13)
- 面试 (10)
- 代码规范 (3)
- Tomcat (12)
- Ajax (5)
- 异常总结 (11)
- REST (2)
- 云 (2)
- RMI (3)
- SOA (1)
- Oracle (12)
- Javascript (20)
- jquery (7)
- JSP自定义标签 (2)
- 电脑知识 (5)
- 浏览器 (3)
- 正则表达式 (3)
- 建站解决问题 (38)
- 数据库设计 (3)
- git (16)
- log4j (1)
- 每天100行代码 (1)
- socket (0)
- java设计模式 耿祥义著 (0)
- Maven (14)
- ibatis (7)
- bug整理 (2)
- 邮件服务器 (8)
- Linux (32)
- TCP/IP协议 (5)
- java多线程并发 (7)
- IO (1)
- 网页小工具 (2)
- Flash (2)
- 爬虫 (1)
- CSS (6)
- JSON (1)
- 触发器 (1)
- java并发 (12)
- ajaxfileupload (1)
- js验证 (1)
- discuz (2)
- Mysql (14)
- jvm (2)
- MyBatis (10)
- POI (1)
- 金融 (1)
- VMWare (0)
- Redis (4)
- 性能测试 (2)
- PostgreSQL (1)
- 分布式 (2)
- Easy UI (1)
- C (1)
- 加密 (6)
- Node.js (1)
- 事务 (2)
- zookeeper (3)
- Spring MVC (2)
- 动态代理 (3)
- 日志 (2)
- 微信公众号 (2)
- IDEA (1)
- 保存他人遇到的问题 (1)
- webservice (11)
- memcached (3)
- nginx (6)
- 抓包 (1)
- java规范 (1)
- dubbo (3)
- xwiki (1)
- quartz (2)
- 数字证书 (1)
- spi (1)
- 学习编程 (6)
- dom4j (1)
- 计算机系统知识 (2)
- JAVA系统知识 (1)
- rpcf (1)
- 单元测试 (2)
- php (1)
- 内存泄漏cpu100%outofmemery (5)
- zero_copy (2)
- mac (3)
- hive (3)
- 分享资料整理 (0)
- 计算机网络 (1)
- 编写操作系统 (1)
- springboot (1)
最新评论
-
masuweng:
亦论一次OutOfMemoryError的定位与解错 -
变脸小伙:
引用[color=red][/color]百度推广中运用的技术 ...
Spring 3 mvc中返回pdf,json,xml等不同的view -
Vanillva:
不同之处是什么??
Mybatis中的like查询 -
thrillerzw:
转了。做个有理想的程序员
有理想的程序员必须知道的15件事 -
liujunhui1988:
觉得很有概括力
15 个必须知道的 Java 面试问题(2年工作经验)
源:http://coderbee.net/index.php/concurrent/20131115/577/comment-page-1#comment-3074
评:
自旋锁(Spin lock)
自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。
自旋锁适用于锁保护的临界区很小的情况,临界区很小的话,锁占用的时间就很短。
简单的实现
import java.util.concurrent.atomic.AtomicReference;
public class SpinLock {
private AtomicReference<Thread> owner = new AtomicReference<Thread>();
public void lock() {
Thread currentThread = Thread.currentThread();
// 如果锁未被占用,则设置当前线程为锁的拥有者
while (owner.compareAndSet(null, currentThread)) {
}
}
public void unlock() {
Thread currentThread = Thread.currentThread();
// 只有锁的拥有者才能释放锁
owner.compareAndSet(currentThread, null);
}
}
SimpleSpinLock里有一个owner属性持有锁当前拥有者的线程的引用,如果该引用为null,则表示锁未被占用,不为null则被占用。
这里用AtomicReference是为了使用它的原子性的compareAndSet方法(CAS操作),解决了多线程并发操作导致数据不一致的问题,确保其他线程可以看到锁的真实状态。
缺点
CAS操作需要硬件的配合;
保证各个CPU的缓存(L1、L2、L3、跨CPU Socket、主存)的数据一致性,通讯开销很大,在多处理器系统上更严重;
没法保证公平性,不保证等待进程/线程按照FIFO顺序获得锁。
Ticket Lock
Ticket Lock 是为了解决上面的公平性问题,类似于现实中银行柜台的排队叫号:锁拥有一个服务号,表示正在服务的线程,还有一个排队号;每个线程尝试获取锁之前先拿一个排队号,然后不断轮询锁的当前服务号是否是自己的排队号,如果是,则表示自己拥有了锁,不是则继续轮询。
当线程释放锁时,将服务号加1,这样下一个线程看到这个变化,就退出自旋。
简单的实现
import java.util.concurrent.atomic.AtomicInteger;
public class TicketLock {
private AtomicInteger serviceNum = new AtomicInteger(); // 服务号
private AtomicInteger ticketNum = new AtomicInteger(); // 排队号
public int lock() {
// 首先原子性地获得一个排队号
int myTicketNum = ticketNum.getAndIncrement();
// 只要当前服务号不是自己的就不断轮询
while (serviceNum.get() != myTicketNum) {
}
return myTicketNum;
}
public void unlock(int myTicket) {
// 只有当前线程拥有者才能释放锁
int next = myTicket + 1;
serviceNum.compareAndSet(myTicket, next);
}
}
缺点
Ticket Lock 虽然解决了公平性的问题,但是多处理器系统上,每个进程/线程占用的处理器都在读写同一个变量serviceNum ,每次读写操作都必须在多个处理器缓存之间进行缓存同步,这会导致繁重的系统总线和内存的流量,大大降低系统整体的性能。
下面介绍的CLH锁和MCS锁都是为了解决这个问题的。
MCS 来自于其发明人名字的首字母: John Mellor-Crummey和Michael Scott。
CLH的发明人是:Craig,Landin and Hagersten。
MCS锁
MCS Spinlock 是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,直接前驱负责通知其结束自旋,从而极大地减少了不必要的处理器缓存同步的次数,降低了总线和内存的开销。
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class MCSLock {
public static class MCSNode {
volatile MCSNode next;
volatile boolean isBlock = true; // 默认是在等待锁
}
volatile MCSNode queue;// 指向最后一个申请锁的MCSNode
private static final AtomicReferenceFieldUpdater UPDATER = AtomicReferenceFieldUpdater
.newUpdater(MCSLock.class, MCSNode.class, "queue");
public void lock(MCSNode currentThread) {
MCSNode predecessor = UPDATER.getAndSet(this, currentThread);// step 1
if (predecessor != null) {
predecessor.next = currentThread;// step 2
while (currentThread.isBlock) {// step 3
}
}
}
public void unlock(MCSNode currentThread) {
if (currentThread.isBlock) {// 锁拥有者进行释放锁才有意义
return;
}
if (currentThread.next == null) {// 检查是否有人排在自己后面
if (UPDATER.compareAndSet(this, currentThread, null)) {// step 4
// compareAndSet返回true表示确实没有人排在自己后面
return;
} else {
// 突然有人排在自己后面了,可能还不知道是谁,下面是等待后续者
// 这里之所以要忙等是因为:step 1执行完后,step 2可能还没执行完
while (currentThread.next == null) { // step 5
}
}
}
currentThread.next.isBlock = false;
currentThread.next = null;// for GC
}
}
CLH锁
CLH锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class CLHLock {
public static class CLHNode {
private boolean isLocked = true; // 默认是在等待锁
}
@SuppressWarnings("unused" )
private volatile CLHNode tail ;
private static final AtomicReferenceFieldUpdater<CLHLock, CLHNode> UPDATER = AtomicReferenceFieldUpdater
. newUpdater(CLHLock.class, CLHNode .class , "tail" );
public void lock(CLHNode currentThread) {
CLHNode preNode = UPDATER.getAndSet( this, currentThread);
if(preNode != null) {//已有线程占用了锁,进入自旋
while(preNode.isLocked ) {
}
}
}
public void unlock(CLHNode currentThread) {
// 如果队列里只有当前线程,则释放对当前线程的引用(for GC)。
if (!UPDATER .compareAndSet(this, currentThread, null)) {
// 还有后续线程
currentThread. isLocked = false ;// 改变状态,让后续线程结束自旋
}
}
}
CLH锁 与 MCS锁 的比较
下图是CLH锁和MCS锁队列图示:
CLH-MCS-SpinLock
差异:
从代码实现来看,CLH比MCS要简单得多。
从自旋的条件来看,CLH是自旋在其他对象的属性,MCS是在本地变量上自旋。
从链表队列来看,CLH的队列是隐式的,CLHNode并不实际持有下一个节点;MCS的队列是物理存在的。
CLH锁释放时只需要改变自己的属性,MCS锁释放则需要改变后继节点的属性。
注意:这里实现的锁都是独占的,且不能重入的。
评:
自旋锁(Spin lock)
自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。
自旋锁适用于锁保护的临界区很小的情况,临界区很小的话,锁占用的时间就很短。
简单的实现
import java.util.concurrent.atomic.AtomicReference;
public class SpinLock {
private AtomicReference<Thread> owner = new AtomicReference<Thread>();
public void lock() {
Thread currentThread = Thread.currentThread();
// 如果锁未被占用,则设置当前线程为锁的拥有者
while (owner.compareAndSet(null, currentThread)) {
}
}
public void unlock() {
Thread currentThread = Thread.currentThread();
// 只有锁的拥有者才能释放锁
owner.compareAndSet(currentThread, null);
}
}
SimpleSpinLock里有一个owner属性持有锁当前拥有者的线程的引用,如果该引用为null,则表示锁未被占用,不为null则被占用。
这里用AtomicReference是为了使用它的原子性的compareAndSet方法(CAS操作),解决了多线程并发操作导致数据不一致的问题,确保其他线程可以看到锁的真实状态。
缺点
CAS操作需要硬件的配合;
保证各个CPU的缓存(L1、L2、L3、跨CPU Socket、主存)的数据一致性,通讯开销很大,在多处理器系统上更严重;
没法保证公平性,不保证等待进程/线程按照FIFO顺序获得锁。
Ticket Lock
Ticket Lock 是为了解决上面的公平性问题,类似于现实中银行柜台的排队叫号:锁拥有一个服务号,表示正在服务的线程,还有一个排队号;每个线程尝试获取锁之前先拿一个排队号,然后不断轮询锁的当前服务号是否是自己的排队号,如果是,则表示自己拥有了锁,不是则继续轮询。
当线程释放锁时,将服务号加1,这样下一个线程看到这个变化,就退出自旋。
简单的实现
import java.util.concurrent.atomic.AtomicInteger;
public class TicketLock {
private AtomicInteger serviceNum = new AtomicInteger(); // 服务号
private AtomicInteger ticketNum = new AtomicInteger(); // 排队号
public int lock() {
// 首先原子性地获得一个排队号
int myTicketNum = ticketNum.getAndIncrement();
// 只要当前服务号不是自己的就不断轮询
while (serviceNum.get() != myTicketNum) {
}
return myTicketNum;
}
public void unlock(int myTicket) {
// 只有当前线程拥有者才能释放锁
int next = myTicket + 1;
serviceNum.compareAndSet(myTicket, next);
}
}
缺点
Ticket Lock 虽然解决了公平性的问题,但是多处理器系统上,每个进程/线程占用的处理器都在读写同一个变量serviceNum ,每次读写操作都必须在多个处理器缓存之间进行缓存同步,这会导致繁重的系统总线和内存的流量,大大降低系统整体的性能。
下面介绍的CLH锁和MCS锁都是为了解决这个问题的。
MCS 来自于其发明人名字的首字母: John Mellor-Crummey和Michael Scott。
CLH的发明人是:Craig,Landin and Hagersten。
MCS锁
MCS Spinlock 是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,直接前驱负责通知其结束自旋,从而极大地减少了不必要的处理器缓存同步的次数,降低了总线和内存的开销。
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class MCSLock {
public static class MCSNode {
volatile MCSNode next;
volatile boolean isBlock = true; // 默认是在等待锁
}
volatile MCSNode queue;// 指向最后一个申请锁的MCSNode
private static final AtomicReferenceFieldUpdater UPDATER = AtomicReferenceFieldUpdater
.newUpdater(MCSLock.class, MCSNode.class, "queue");
public void lock(MCSNode currentThread) {
MCSNode predecessor = UPDATER.getAndSet(this, currentThread);// step 1
if (predecessor != null) {
predecessor.next = currentThread;// step 2
while (currentThread.isBlock) {// step 3
}
}
}
public void unlock(MCSNode currentThread) {
if (currentThread.isBlock) {// 锁拥有者进行释放锁才有意义
return;
}
if (currentThread.next == null) {// 检查是否有人排在自己后面
if (UPDATER.compareAndSet(this, currentThread, null)) {// step 4
// compareAndSet返回true表示确实没有人排在自己后面
return;
} else {
// 突然有人排在自己后面了,可能还不知道是谁,下面是等待后续者
// 这里之所以要忙等是因为:step 1执行完后,step 2可能还没执行完
while (currentThread.next == null) { // step 5
}
}
}
currentThread.next.isBlock = false;
currentThread.next = null;// for GC
}
}
CLH锁
CLH锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class CLHLock {
public static class CLHNode {
private boolean isLocked = true; // 默认是在等待锁
}
@SuppressWarnings("unused" )
private volatile CLHNode tail ;
private static final AtomicReferenceFieldUpdater<CLHLock, CLHNode> UPDATER = AtomicReferenceFieldUpdater
. newUpdater(CLHLock.class, CLHNode .class , "tail" );
public void lock(CLHNode currentThread) {
CLHNode preNode = UPDATER.getAndSet( this, currentThread);
if(preNode != null) {//已有线程占用了锁,进入自旋
while(preNode.isLocked ) {
}
}
}
public void unlock(CLHNode currentThread) {
// 如果队列里只有当前线程,则释放对当前线程的引用(for GC)。
if (!UPDATER .compareAndSet(this, currentThread, null)) {
// 还有后续线程
currentThread. isLocked = false ;// 改变状态,让后续线程结束自旋
}
}
}
CLH锁 与 MCS锁 的比较
下图是CLH锁和MCS锁队列图示:
CLH-MCS-SpinLock
差异:
从代码实现来看,CLH比MCS要简单得多。
从自旋的条件来看,CLH是自旋在其他对象的属性,MCS是在本地变量上自旋。
引用
CLH是在前驱节点的属性上自旋,而MCS是在本地属性变量上自旋。
从链表队列来看,CLH的队列是隐式的,CLHNode并不实际持有下一个节点;MCS的队列是物理存在的。
CLH锁释放时只需要改变自己的属性,MCS锁释放则需要改变后继节点的属性。
注意:这里实现的锁都是独占的,且不能重入的。
发表评论
-
自旋锁、排队自旋锁、MCS锁、CLH锁
2016-02-19 17:08 503源:http://coderbee.net/index.php ... -
java.util.concurrent.locks.LockSupport
2014-12-28 19:56 614源:http://my.oschina.net/readjav ... -
Spring线程池开发实战
2014-12-12 10:44 499源:http://blog.csdn.net/chszs/ar ... -
高并发无锁实现代码块只进入一次小技巧
2014-10-29 14:10 842源:http://kenwublog.com/conc ... -
Java偏向锁实现原理(Biased Locking)
2014-10-21 17:51 1296源:http://kenwublog.com/theory-o ... -
Java 6 JVM参数选项大全(中文版)
2014-10-21 17:51 643源:http://kenwublog.com/docs/jav ... -
Java轻量级锁原理详解(Lightweight Locking)
2014-10-21 17:50 676源:http://kenwublog.com/theo ... -
08.JAVA同步块
2014-01-09 21:03 0源:http://ifeve.com/synchronized ... -
07.线程安全及不可变性
2014-01-09 21:02 0源:07.线程安全及不可变性 评: 原文链接 作者:Jakob ... -
06.线程安全与共享资源
2014-01-09 21:02 0源:http://ifeve.com/thread-safet ... -
05.竞态条件与临界区
2014-01-09 21:01 0源:http://ifeve.com/race-conditi ... -
04.如何创建并运行java线程
2014-01-09 20:58 454源:http://ifeve.com/creating-and ... -
03.多线程的代价
2014-01-09 20:57 419源:http://ifeve.com/costs-of ... -
02.多线程的优点
2014-01-09 20:56 371源:http://ifeve.com/benefits/ 评: ... -
01.Java并发性和多线程介绍
2014-01-09 11:11 672源:http://ifeve.com/java-con ...
相关推荐
自旋锁的基本思想是当一个线程试图获取已被其他线程持有的锁时,它不会立即阻塞,而是不断地循环检查锁的状态,直到锁变为可用状态为止,这个过程称为“自旋”。在Linux内核中,自旋锁是实现内核级并发的重要工具。 ...
一旦一个线程获得了互斥体的锁定,其他试图获取该互斥体的线程将被阻塞,直到当前持有者释放锁为止。互斥体特别适用于线程间的同步控制,不适用于进程间通信。 #### 四、自旋锁 自旋锁(Spin Lock)是另一种常见的...
自旋锁的设计理念在于,当一个线程尝试获取已被其他线程持有的自旋锁时,它不会进入睡眠状态,而是会不断地进行忙循环,检查锁的状态,直到锁被释放。这种行为被称为“自旋”,因此得名自旋锁。 自旋锁的特点在于其...
2. **获取自旋锁**:`spin_lock()`用于获取自旋锁,如果锁已被占用,调用者将不断地检查锁的状态,直到锁被释放。 3. **尝试获取自旋锁**:`spin_trylock()`提供了一种非阻塞的方式尝试获取自旋锁,如果锁无法立即...
传统的互斥锁(Mutex)是一种常见的同步机制,它通过让一个线程获得锁并独占资源,其他线程则等待该线程释放锁来实现线程间的同步。然而,在高并发环境下,互斥锁可能会成为性能瓶颈,因为它可能导致线程频繁地上...
当一个线程尝试获取一个已被占用的自旋锁时,它会进入一个循环,不断检查锁是否已经释放,而不是像互斥锁那样挂起线程并让出处理器。这在多处理器系统中是有意义的,因为在多核环境中,另一个持有锁的线程可能就在同...
自旋锁是一种特殊的锁机制,当一个线程试图获取已被其他线程持有的锁时,它会不断地“自旋”检查锁的状态,而不会被调度出去。一旦锁被释放,自旋停止,线程立即获得锁。自旋锁适用于持有时间短且竞争激烈的场景,...
自旋锁的原理是,当一个线程试图获取一个已经被其他线程持有的锁时,它不会像普通互斥锁那样进入睡眠状态,而是会不断地循环检查锁的状态,直至锁变为可用状态。这种机制在处理器等待时间短且锁持有时间也很短的情况...
在Linux内核中,自旋锁是内核级的同步机制,当一个线程试图获取已被其他线程持有的锁时,它会进入“自旋”状态,不断地检查锁是否释放,而不会将控制权交还给调度器。这样做的优点是在大多数情况下,一旦锁被释放,...
自旋锁的特性决定了持有锁的线程如果发现资源被占用,它会不断地循环检查("自旋"),直到锁被释放。 在ZYNQ 7010-7020上实现自旋锁测试,首先我们需要理解Linux驱动模型。Linux驱动程序通常分为字符设备驱动、块...
可重入锁也称为递归锁,它允许同一个线程多次进入被该锁保护的代码块。实现可重入锁的关键在于记录当前持有锁的线程以及锁的计数器,每当线程获取锁时,计数器就会递增,释放锁时递减,直到计数器归零,锁才被其他...
这意味着,当一个线程尝试获取已被持有的自旋锁时,它会不断地检查锁的状态,直到锁变为可用。这种机制在多处理器环境下特别有用,因为线程可以在等待锁的过程中保持活跃,一旦锁被释放,就能立即获得并继续执行。 ...
当一个线程获取到锁时,其他试图获取锁的线程将进入“自旋”状态,不断地检查锁是否释放,一旦锁被释放,自旋的线程就会立即获得锁并继续执行,这样可以减少线程上下文切换的开销。 4. **代码实现与配置**: 在...
4. **spin_trylock(spinlock_t *lock)**:尝试获取自旋锁,如果锁被其他线程持有,则立即返回失败。 **示例**: 在实时系统中,当需要保护某个短暂的关键代码段时,可以使用自旋锁。这样可以确保即使在高负载下,...
自旋锁的基本思想是当一个线程试图获取已被其他线程持有的锁时,它会不断地检查锁的状态,即“自旋”等待,直到锁被释放为止。由于自旋锁不会导致调用者进入睡眠状态,因此它主要适用于那些持有锁的时间非常短的操作...
在计算机系统中,当一个线程尝试获取已经被其他线程持有的锁时,自旋锁会使得该线程“自旋”等待,而不是被挂起。自旋锁的主要目标是减少线程上下文切换的开销,提高系统的并行性能。 本文将深入探讨自旋锁的实现...
自旋锁是一种特殊的锁机制,当锁被占用时,尝试获取锁的线程不会进入睡眠状态,而是不断地循环检查锁是否可用,即“自旋”。这种机制在处理短时锁时效率较高,因为线程无需从内核态切换到用户态。Linux内核提供了...
- 自旋锁(Spin Lock):当一个线程尝试获取已经被其他线程持有的锁时,它会进行短时间的循环等待,而不是立即进入阻塞状态。 - 偏向锁(Biased Locking):偏向锁通过在Mark Word中记录持有偏向锁的线程ID来避免...
自旋锁是另一种同步原语,当锁被占用时,尝试获取锁的线程会不断地检查锁的状态,而不是进入睡眠状态。在Linux中,可以使用`pthread_spinlock_t`类型和`pthread_spin_lock()`、`pthread_spin_unlock()`等函数来实现...