一 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)空间。

最新文章

  1. shell语法
  2. 判断.net中在windows系统下的字节序
  3. 使用JVMTI创建调试和监控代理
  4. JS~~~ 前端开发一些常用技巧 模块化结构 &amp;&amp;&amp;&amp;&amp; 命名空间处理 奇技淫巧!!!!!!
  5. iOS-数据持久化-偏好设置
  6. [LeetCode]题解(python):043-Multiply Strings
  7. Android 中使用自定义字体的方法
  8. HTML5----CSS显示半个字符
  9. OpenGL立方体在世界坐标系中_缩放_旋转_平移_顶点片源着色器_光照作用_棋盘纹理贴图
  10. 4 weekend110的hive入门
  11. MySQL数据库触发器(trigger)
  12. OCP读书笔记(17) - 计划任务
  13. __new__()方法的使用和实例化
  14. Spring Boot 2.0 教程 | 配置 Undertow 容器
  15. Ubuntu 18.04安装中文输入法
  16. Linux 小知识翻译 - 目录 (完结)
  17. python pandas模块,nba数据处理(1)
  18. CSS动画原理及硬件加速
  19. HDU 5727 - Necklace - [全排列+二分图匹配][Hopcroft-Karp算法模板]
  20. freemarker在js中的应用

热门文章

  1. JavaScript学习笔记(三)——留言板知操纵DOM节点
  2. Educational Codeforces Round 23.C
  3. 2017寒假零基础学习Python系列之函数之 函数之定义可变参数
  4. VBS连接远程Oracle
  5. localStorage与location的用法
  6. 用Left join代替not in
  7. Java探秘之神秘的字符串String(二)
  8. GCD使用汇总
  9. SLAM中的优化理论(一)—— 线性最小二乘
  10. JDBC&amp;&amp;c3p0、事务、批处理、多线程 于一体的经典秘方QueryRunner