Synchronized和Lock, 以及自旋锁 Spin Lock, Ticket Spin Lock, MCS Spin Lock, CLH Spin Lock
Synchronized和Lock
synchronized是一个关键字, Lock是一个接口, 对应有多种实现. 使用synchronized进行同步和使用Lock进行同步的区别
- 使用synchronized同步时, 未获得锁的进程只能等待. 而使用Lock进行同步时, 有多种选择: 例如用读写锁区分不同的同步需求, 用tryLock使未获得锁的线程立即返回或在一段时间后返回, 或者在等待时可以随时响应中断后返回.
- 使用synchronized无法知道线程是否成功获取到锁, 使用Lock可以
- synchronized不需要手动释放锁, 在代码块执行结束或发生异常时, jvm会让线程自动释放锁. 而Lock需要手动释放锁, 如果有未释放的情况, 就会出现死锁.
Spin Lock
自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。自旋锁适用于锁保护的临界区很小的情况,临界区很小的话,锁占用的时间就很短。
public class Spinlock {
private AtomicReference<Thread> owner = new AtomicReference<>();
private void lock() {
Thread current = Thread.currentThread();
while (!owner.compareAndSet(null, current)) {
}
}
private void unlock() {
Thread current = Thread.currentThread();
owner.compareAndSet(current, null);
}
}
Ticket Spin Lock
为了解决Spin Lock中随机不公平的问题, 使用排队自旋锁
public class TicketSpinlock {
private AtomicInteger ticket = new AtomicInteger();
private AtomicInteger inService = new AtomicInteger();
public int lock() {
int myTicket = ticket.getAndIncrement();
while (myTicket != inService.get()) {
}
return myTicket;
}
// 只有持有锁的才能释放锁
public void unlock(int ticket) {
int next = ticket + 1;
inService.compareAndSet(ticket, next);
}
}
MCS Spin Lock
MCS锁是基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,直接前驱负责通知其结束自旋, 减少不必要的处理器缓存同步的次数,降低总线和内存的开销.
public class McsSpinlock {
public static class McsNode {
volatile McsNode next;
volatile boolean isBlock = true; // 默认是在等待锁
}
volatile McsNode queue;// 指向最后一个申请锁的MCSNode
private static final AtomicReferenceFieldUpdater<McsSpinlock, McsNode> UPDATER =
AtomicReferenceFieldUpdater.newUpdater(McsSpinlock.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
}
} else { // 只有一个线程在使用锁,没有前驱来通知它,所以得自己标记自己为非阻塞
currentThread.isBlock = false;
}
}
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 Spin Lock
基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。
public class ClhSpinlock {
public static class ClhNode {
private volatile boolean isLocked = true; // 默认在等待
}
private volatile ClhNode tail ;
private static final AtomicReferenceFieldUpdater<ClhSpinlock, ClhNode> UPDATER =
AtomicReferenceFieldUpdater. newUpdater(ClhSpinlock.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 ;// 改变状态,让后续线程结束自旋
}
}
}
JDK Locks: ReentrantLock
JDK的concurrent.locks包下的ReentrantLock, 用于实现响应中断的非阻塞锁, 里面模仿CLH Lock的机制, 实现了公平队列和非公平队列. 官方文档里使用了一个互相鞠躬的例子来举例ReentrantLock的使用
public class Safelock {
static class Friend {
private final String name;
private final Lock lock = new ReentrantLock();
public Friend(String name) {this.name = name;}
public String getName() {return this.name;}
public boolean impendingBow(Friend bower) {
// 非阻塞, 当两个锁都拿到时, 才bowback
Boolean myLock = false;
Boolean yourLock = false;
try {
myLock = lock.tryLock();
yourLock = bower.lock.tryLock();
} finally {
if (!(myLock && yourLock)) {
// 此步拿到的锁, 不用了要立即释放
if (myLock) {
lock.unlock();
}
if (yourLock) {
bower.lock.unlock();
}
}
}
return myLock && yourLock;
}
public void bow(Friend bower) {
if (impendingBow(bower)) {
try {
System.out.format("%s: %s has bowed to me!%n", bower.getName(), this.name);
bower.bowBack(this);
} finally {
// 用完后及时释放锁
lock.unlock();
bower.lock.unlock();
}
} else {
System.out.format(
"%s: %s wanted to bow to me, but saw that I was bowing.%n",
bower.getName(), this.name);
}
}
public void bowBack(Friend bower) {
System.out.format("%s: %s has bowed back to me!%n", bower.getName(), this.name);
}
}
static class BowLoop implements Runnable {
private Friend bower;
private Friend bowee;
public BowLoop(Friend bower, Friend bowee) {
this.bower = bower;
this.bowee = bowee;
}
public void run() {
Random random = new Random();
for (; ; ) {
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException e) {}
bowee.bow(bower);
}
}
}
public static void main(String[] args) {
final Friend alphonse =
new Friend("Alphonse");
final Friend gaston =
new Friend("Gaston");
final Friend hudson =
new Friend("Hudson");
new Thread(new BowLoop(alphonse, hudson)).start();
new Thread(new BowLoop(hudson, gaston)).start();
new Thread(new BowLoop(gaston, alphonse)).start();
}
}
Synchronized和Lock, 以及自旋锁 Spin Lock, Ticket Spin Lock, MCS Spin Lock, CLH Spin Lock的更多相关文章
- Synchronized的原理及自旋锁,偏向锁,轻量级锁,重量级锁的区别(摘抄和理解)
1. 自旋锁的优缺点: 自旋锁 自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持 ...
- 自旋锁Spin lock与互斥锁Mutex的区别
POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套常用的API.线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用 ...
- 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁
1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...
- 可重入锁 & 自旋锁 & Java里的AtomicReference和CAS操作 & Linux mutex不可重入
之前还是写过蛮多的关于锁的文章的: http://www.cnblogs.com/charlesblc/p/5994162.html <[转载]Java中的锁机制 synchronized &a ...
- 自旋锁、排队自旋锁、MCS锁、CLH锁
转载自:http://coderbee.net/index.php/concurrent/20131115/577 自旋锁(Spin lock) 自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他 ...
- Pthread spinlock自旋锁
锁机制(lock) 是多线程编程中最常用的同步机制,用来对多线程间共享的临界区(Critical Section) 进行保护. Pthreads提供了多种锁机制,常见的有:1) Mutex(互斥量): ...
- 二、多线程基础-乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁
1.10乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将 比较-设置 ...
- Optimistic concurrency control 死锁 悲观锁 乐观锁 自旋锁
Optimistic concurrency control https://en.wikipedia.org/wiki/Optimistic_concurrency_control Optimist ...
- Linux 自旋锁,互斥量(互斥锁),读写锁
自旋锁(Spin Lock) 自旋锁类似于互斥量,不过自旋锁不是通过休眠阻塞进程,而是在取得锁之前一直处于忙等待的阻塞状态.这个忙等的阻塞状态,也叫做自旋. 自旋锁通常作为底层原语实现其他类型的锁. ...
随机推荐
- 解决TextView排版混乱或者自动换行的问题
其实在TextView中遇到排版自动换行而导致混乱不堪的情况是非常常见的,而且导致这种问题产生的原因就是英文和中文混合输入,半角字符和全角字符混合在一起了.一般情况下,我们输入的数字.字母以及英文标点 ...
- KJBitmap与KJHttp的深度用法
摘要 本文原创,转载请注明地址:http://kymjs.com/code/2015/09/24/01这篇文章是对KJFrameForAndroid使用的一个深入学习 之前写过一些有关KJFrame的 ...
- python接口自动化4-绕过验证码登录(cookie)
前言 有些登录的接口会有验证码:短信验证码,图形验证码等,这种登录的话验证码参数可以从后台获取的(或者查数据库最直接). 获取不到也没关系,可以通过添加cookie的方式绕过验证码. 一.抓登录coo ...
- kafka深入研究(六)
Kafka Producer端封装自定义消息 Kafka.network包源码解读 Kafka Consumer端的一些解惑 Kafka producer使用注意 kafka0.8的一些变动,先收藏, ...
- 简明python教程 --C++程序员的视角(三):模块
模块和包 1 python程序由包(package).模块(module)和函数组成.包是由一系列模块组成的集合.模块是处理某一类问题的函数和类的集合.函数是一段可以重复多次调用的代码. 2 pyth ...
- NFS CIFS SAMBA 的联系和区别
Common Internet File System, CIFS Server Message Block, SMB Network File System, NFS 在早期网络世界当中,档案数据在 ...
- 【Codeforces】【#295】【Div.2】
o(︶︿︶)o 唉跪烂了…… B题由于考虑的不周全WA了3次…… C题由于#include了<cmath>,而我函数声明的是pow(LL a,LL b)但调用的时候 [没!有!把!n!的! ...
- uestc 360(区间合并)
题意:有一个长度为n的序列.然后有两种操作,Q a b是输出区间a b内最长上升子序列的长度.A a b c是把区间a b内全部数字加上c. 题解:用线段树维护区间的最长上升子序列长度,那么一个区间的 ...
- Python源码学习七 .py文件的解释
Python源码太复杂了... 今天看了下对.py文件的parse, 云里雾里的 py文件是最简单的, 在python的交互式窗口 import这个模块 a = 10 print(a) 开始分析,堆栈 ...
- 性能二 fortnite unreal opt
https://replay.unrealsummit.co.kr/data2018/usm2018_42.pdf?ckattempt=1 https://www.unrealengine.com/e ...