一 Test-and-Set Lock

所谓测试设置是最基本的锁,每个线程共用一把锁,进入临界区之前看没有有线程在临界区,如果没有,则进入,并上锁,如果有则等待。java实践中利用了原子的设置state变量来保证一次只有一个线程可以获得到锁。

public class TASLock implements Lock {
AtomicBoolean state = new AtomicBoolean(false); @Override
public void lock() {
while (state.getAndSet(true)) { }
}
@Override
public void unlock() {
state.set(false);
}
}

这种锁优点就是简单,缺点是在硬件层面上读取state时候,如果在cache中命中,那么直接从cache中读取就行。但是没有命中,那么将在总线产生一个广播,如果在其他处理器中的cache中找到该地址,那么就以该地址的值做为响应,并且广播该值。更糟糕的是每一个进入自旋的线程都会产生cache缺失,这样产生大量广播流量,延迟较长。

二 指数后退lock

TASLock如果产生大量自旋的线程,则效率很低,避免这个问题就是给后进入自旋的线程一个延迟避让。避让策略有很多种,这里选择指数回退。

class Backoff {
int max;
int min;
int limit;
public Backoff(int min, int max, int limit) {
this.max = max;
this.min = min;
this.limit = limit;
}
public void backoff() {
int delay = new Random().nextInt(limit);
limit = Math.min(max, 2 * limit);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class BackoffLock implements Lock{
private AtomicBoolean state = new AtomicBoolean(false); @Override
public void lock() {
Backoff backoff = new Backoff(1000, 5000, 100);
while (true) {
while (state.get()) {};
if (!state.getAndSet(true)) {
return;
} else {
backoff.backoff();
}
}
}
@Override
public void unlock() {
state.set(false);
}
}

三 基于数组的简单队列锁

上面基于TAS的lock并没有真正解决cache缺失的流量问题,所以利用java的ThreadLocal为每一个线程存储一个本地标识索引对应于是否进入临界区而不是在共享的变量上自旋,这样cache流量问题就能得到解决。

public class Alock implements Lock {
ThreadLocal<Integer> mysolitindex = new ThreadLocal<Integer>() {
protected Integer initvalue() {
return 0;
}
};
AtomicInteger tail;
boolean[] flag;
int size;
Alock (int capacity) {
size = capacity;
tail = new AtomicInteger(0);
flag = new boolean[capacity];
flag[0] = true;
}
@Override
public void lock() {
int solt = tail.getAndIncrement() % size;
mysolitindex.set(solt);
while (!flag[solt]) {};
}
}

四 改进的Alock--CLH队列锁

ALock缺点在于并发线程数量固定,空间开销比较大,每次必须分配固定数量的本地线程变量和共享变量。CLHlock解决了空间问题,它利用threadlocal保持2个指针指向pre和current来实现一个隐式的链表,并且通过pre使得cache命中率提高。

class CLHLock implements Lock {
AtomicReference<Qnode> tail;
ThreadLocal<Qnode> myNode
= new Qnode();
public void lock() {
Qnode pred
= tail.getAndSet(myNode);
while (pred.locked) {}
}}
public void unlock() {
myNode.locked.set(false);
myNode = pred;
}
}
class Qnode {
AtomicBoolean locked =
new AtomicBoolean(true);
}

本地线程中保持这指向qnode的指针mynode。如果有L个锁,n个线程,并且每个线程最多同时访问一个锁,那么需要O(L+n)空间。

java自旋锁的更多相关文章

  1. 自旋锁原理及java自旋锁

    一.自旋锁的概念 首先是一种锁,与互斥锁相似,基本作用是用于线程(进程)之间的同步.与普通锁不同的是,一个线程A在获得普通锁后,如果再有线程B试图获取锁,那么这个线程B将会挂起(阻塞):试想下,如果两 ...

  2. 并发编程--CAS自旋锁

    在前两篇博客中我们介绍了并发编程--volatile应用与原理和并发编程--synchronized的实现原理(二),接下来我们介绍一下CAS自旋锁相关的知识. 一.自旋锁提出的背景 由于在多处理器系 ...

  3. Java锁之自旋锁详解

    锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类 ...

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

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

  5. Java并发框架——AQS堵塞队列管理(一)——自旋锁

    我们知道一个线程在尝试获取锁失败后将被堵塞并增加等待队列中,它是一个如何的队列?又是如何管理此队列?这节聊聊CHL Node FIFO队列.  在谈到CHL Node FIFO队列之前,我们先分析这样 ...

  6. Java线程并发中常见的锁--自旋锁 偏向锁

    随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想 ...

  7. Java并发框架——AQS阻塞队列管理(一)——自旋锁

    我们知道一个线程在尝试获取锁失败后将被阻塞并加入等待队列中,它是一个怎样的队列?又是如何管理此队列?这节聊聊CHL Node FIFO队列. 在谈到CHL Node FIFO队列之前,我们先分析这种队 ...

  8. Java 多线程之自旋锁

    一.什么是自旋锁? 自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环. 获取锁的线 ...

  9. Java锁的种类以及辨析(二):自旋锁的其他种类

    作者:山鸡 锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具 ...

随机推荐

  1. Java基础(2)-基础类型

    java基础类型 基础类型 package knowledge.base; public class Properties { /** * 整型 * int 4字节 -2 147 483 648 ~2 ...

  2. LR监控Windows Server 2008 R2系统资源提示“指定的网络名不可用。”

    问题现象: LR监控远程服务器Window Server 2008 R2 系统资源,提示“Monitor name :Windows Resources. Cannot connect to mach ...

  3. Linux进阶命令-sort、uniq、 cut、sed、grep、find、awk

    命令难度总体来说有简入难,参数都是工作中常常用到的.如果涉及到一些生僻的参数还请百度或man一下. sort(参考学习网站:http://www.cnblogs.com/dong008259/arch ...

  4. JDBC02 利用JDBC连接数据库【使用数据库连接池】

    目录 1/2/3  Statement 和 Preparedstatement 的区别 4 读取properties配置文件 5 数据库连接池 6 利用数据库连接池连接数据库 1 使用Statemen ...

  5. JFile的导入xlsx与xls

    首先需要有JAVA的一些jar包 下载地址:http://download.csdn.net/detail/qq_35980546/9892511 你要先配置好路由,还有能拿到绝对路径才行 下面直接给 ...

  6. Java基础语法<七> 对象与类

    笔记整理 来源于<Java核心技术卷 I > <Java编程思想> 1. 类之间的关系 依赖 users– a 是一种最明显的.最常见的关系.如果一个类的方法操作另一个类的对象 ...

  7. spring框架-spring.xml配置文件

    运行的时候会报错的,因为写到<bean>标签里面去了,肯定会报错的,要记得把注释删掉,就不会报错了,这样写注释是为了方便下次自己看. <?xml version="1.0& ...

  8. Git 使用问题 - win7 git bash下git pull失败

    win7 旗舰版,从github上pull代码时,git bash命令出现错误 Administrator@rust-PC /g/rust_proj/cardslib (master) $ git - ...

  9. java泛型(整理)

    1 泛型基础知识 泛型需要理解两个关键点:1)类型擦除 2)类型转换 1)类型擦除 泛型有个很重要的概念,是类型擦除.正确理解泛型概念的首要前提是理解类型擦除(type erasure). Java中 ...

  10. MVC 路由设置伪静态

    public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/ ...