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的更多相关文章

  1. Synchronized的原理及自旋锁,偏向锁,轻量级锁,重量级锁的区别(摘抄和理解)

    1. 自旋锁的优缺点: 自旋锁 自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持 ...

  2. 自旋锁Spin lock与互斥锁Mutex的区别

    POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套常用的API.线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用 ...

  3. 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁

    1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...

  4. 可重入锁 & 自旋锁 & Java里的AtomicReference和CAS操作 & Linux mutex不可重入

    之前还是写过蛮多的关于锁的文章的: http://www.cnblogs.com/charlesblc/p/5994162.html <[转载]Java中的锁机制 synchronized &a ...

  5. 自旋锁、排队自旋锁、MCS锁、CLH锁

    转载自:http://coderbee.net/index.php/concurrent/20131115/577 自旋锁(Spin lock) 自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他 ...

  6. Pthread spinlock自旋锁

    锁机制(lock) 是多线程编程中最常用的同步机制,用来对多线程间共享的临界区(Critical Section) 进行保护. Pthreads提供了多种锁机制,常见的有:1) Mutex(互斥量): ...

  7. 二、多线程基础-乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁

    1.10乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将 比较-设置 ...

  8. Optimistic concurrency control 死锁 悲观锁 乐观锁 自旋锁

    Optimistic concurrency control https://en.wikipedia.org/wiki/Optimistic_concurrency_control Optimist ...

  9. Linux 自旋锁,互斥量(互斥锁),读写锁

    自旋锁(Spin Lock) 自旋锁类似于互斥量,不过自旋锁不是通过休眠阻塞进程,而是在取得锁之前一直处于忙等待的阻塞状态.这个忙等的阻塞状态,也叫做自旋. 自旋锁通常作为底层原语实现其他类型的锁. ...

随机推荐

  1. tomcat 启动时设置 java 参数,mark

    在文件  startup.bat/.sh 中添加 set "JAVA_OPTS=-Xms2048m -Xmx4096m -XX:NewSize=256m -XX:MaxNewSize=102 ...

  2. Windows Embedded Compact 7初体验

    Windows Embedded Compact 7初体验 Windows Embedded Compact 7已经出来半年多了,一直没时间搞.最近它又出了Refresh的版本,电脑也换了个1T的硬盘 ...

  3. Web Sessions Installation

    展示不使用Terracotta DSO如何集群Web Sessions. 1.要求 1)jdk1.6或者更高版本 2)Terracotta 3.7或者更高版本 3)所有被集群的对象需要实现序列化,如果 ...

  4. JS动态改变select选择变更option的index值

    document.getElementById("louyuming").options[0].selected=true; function jsSelectIsExitItem ...

  5. java 数据流的处理

    字节流类 功能简单介绍 DataInputStream 包含了读取Java标准数据类型的输入流 DataOutputStream 包含了写Java标准数据类型的输出流 ByteArrayInputSt ...

  6. 如何设置nginx日志格式来查看负载分担结果

     转载:http://www.cnblogs.com/LoveJulin/p/5082363.html nginx配置好负载分担后,测试的时候,如何查看负载分担情况:通过设置nginx日志显示: ng ...

  7. 21.重入锁ReentrantLock

    ReentrantLock     重入锁,在需要进行代码同步部分上加锁,但是一定要记得解锁.     类型:公平锁(队列方式进行排队).非公平锁(按照cpu的分配),非公平锁性能要比公平锁性能高,默 ...

  8. 【BZOJ】【3240】【NOI2013】矩阵游戏

    十进制快速幂+矩阵乘法+常数优化 听说这题还可以强行算出来递推式……然后乘乘除除算出来…… 然而蒟蒻选择了一个比较暴力的做法= = 我们发现这个递推的过程是线性的,所以可以用矩阵乘法来表示,$x=a* ...

  9. go语言基础之copy的使用

    1.copy的使用 示例: package main //必须有个main包 import "fmt" func main() { srcSlice := []int{1, 2} ...

  10. js判断是否为手机访问

    JavaScript判断浏览器类型一般有两种办法,一种是根据各种浏览器独有的属性来分辨,另一种是通过分析浏览器的userAgent属性来判断的.在许多情况下,值判断出浏览器类型之后,还需判断浏览器版本 ...