首先向Doug Lea致敬。

CLH

以下是CLH锁的一个简单实现:

class SimpleCLHLock {
/**
* initialized with a dummy node
*/
private Node dummy = new Node();
private AtomicReference<Node> tail = new AtomicReference<Node>(dummy); /**
* implicit single linked list node
*/
private static class Node {
public volatile boolean locked = false;
} private ThreadLocal<Node> threadLockNode = new ThreadLocal<Node>(); public void lock() {
Node last_tail;
Node new_tail = new Node();
new_tail.locked = true; while (true) {
last_tail = tail.get(); if (tail.compareAndSet(last_tail, new_tail)) {
break;
}
}
// we just keep previous tail node as current node's direct predecessor
// and waiting it jump into unlocked state, so we form a single linked list implicitly
while (last_tail.locked) {
Thread.yield();
} // we need the node reference when we want to unlock
// some threads may waiting on its state to be unlocked in above while-loop statement.
threadLockNode.set(new_tail);
} public void unlock() {
// retrieve the lock state held by current thread
Node node = threadLockNode.get();
// if null then the current thread has not own the lock, also can't perform unlock operation
if (node == null) {
throw new IllegalMonitorStateException();
}
node.locked = false;
threadLockNode.set(null);
}
}

测试了不同的同步方式的速度

package lock;

import java.util.List;

import java.util.ArrayList;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock; /**
* Created by hegaofeng on 6/30/15.
*/
public class CLH {
private static volatile long count; private static AtomicLong atomicLong = new AtomicLong(); private static ExecutorService pool = Executors.newCachedThreadPool(); public static void main(String[] args) { final int deltaPerThreads = 10000;
final int numsOfThreads = 10; Callable<Boolean> raw_counter = new Callable<Boolean>() {
@Override
public Boolean call() {
for (int i=0; i<deltaPerThreads; i++) count++;
return true;
}
}; Callable<Boolean> syn_counter = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
synchronized (CLH.class) {
count++;
}
} return true;
}
}; Callable<Boolean> clh_counter = new Callable<Boolean>() {
private SimpleCLHLock lock = new SimpleCLHLock();
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
lock.lock();
count++;
lock.unlock();
}
return true;
}
}; final Callable<Boolean> lck_counter = new Callable<Boolean>() {
private ReentrantLock lock = new ReentrantLock();
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
lock.lock();
count++;
lock.unlock();
}
return true;
}
}; final Callable<Boolean> lck_fair_counter = new Callable<Boolean>() {
private ReentrantLock lock = new ReentrantLock(true);
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
lock.lock();
count++;
lock.unlock();
}
return true;
}
}; Callable<Boolean> sem_counter = new Callable<Boolean>() {
private Semaphore sem = new Semaphore(1);
@Override
public Boolean call() throws Exception {
for(int i=0; i<deltaPerThreads; i++) {
sem.acquire();
count++;
sem.release();
}
return true;
}
}; Callable<Boolean> sem_fair_counter = new Callable<Boolean>() {
private Semaphore sem = new Semaphore(1, true);
@Override
public Boolean call() throws Exception {
for(int i=0; i<deltaPerThreads; i++) {
sem.acquire();
count++;
sem.release();
}
return true;
}
}; Callable<Boolean> cas_counter = new Callable<Boolean>() {
private AtomicBoolean locked = new AtomicBoolean();
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
while (true) {
if (locked.compareAndSet(false, true)) {
count++;
locked.getAndSet(false);
break;
}
}
}
return true;
}
}; Callable<Boolean> pure_cas = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
for (int i=0; i<deltaPerThreads; i++) {
atomicLong.incrementAndGet();
}
return true;
}
}; test("raw", raw_counter, deltaPerThreads, numsOfThreads); test("syn", syn_counter, deltaPerThreads, numsOfThreads); test("CLH", clh_counter, deltaPerThreads, numsOfThreads); test("lock", lck_counter, deltaPerThreads, numsOfThreads); test("lock_fair", lck_fair_counter, deltaPerThreads, numsOfThreads); test("sem", sem_counter, deltaPerThreads, numsOfThreads); test("sem_fair", sem_fair_counter, deltaPerThreads, numsOfThreads); test("CAS", cas_counter, deltaPerThreads, numsOfThreads); test("pure_CAS", pure_cas, deltaPerThreads, numsOfThreads);
} public static void test(String name, Callable<Boolean> callable, final int deltaPerThreads, final int numsOfThreads) {
count = 0;
atomicLong.set(0);
pool = Executors.newCachedThreadPool(); List<Callable<Boolean>> calls = new ArrayList<Callable<Boolean>>();
for (int i=0; i<numsOfThreads; i++) {
calls.add(callable);
}
long start = System.nanoTime();
try {
pool.invokeAll(calls);
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.shutdown();
long end = System.nanoTime();
System.out.println("case : " + name);
System.out.println("counted: " + (name.equals("pure_CAS") ? atomicLong.get() : count));
System.out.println("target : " + numsOfThreads * deltaPerThreads);
System.out.println("time : " + (end - start) / 1e6 + "ms");
System.out.println();
}
}

结果:

case   : raw
counted: 79270
target : 100000
time : 11.278ms case : syn
counted: 100000
target : 100000
time : 13.553ms case : CLH
counted: 100000
target : 100000
time : 1037.665ms case : lock
counted: 100000
target : 100000
time : 29.134ms case : lock_fair
counted: 100000
target : 100000
time : 638.944ms case : sem
counted: 100000
target : 100000
time : 45.154ms case : sem_fair
counted: 100000
target : 100000
time : 552.855ms case : CAS
counted: 100000
target : 100000
time : 158.422ms case : pure_CAS
counted: 100000
target : 100000
time : 31.964ms
  • raw 表示不进行任何同步,现在没有保护的情况下计数累加产生了错误
  • syn 表示使用内置锁同步方式即使用synchronized块,在几种方式里表现最好
  • CLH 表示使用自己实现的一个CLH锁(具有排队功能的自旋锁),这里的CLH的实现方式决定了它是一个公平锁
  • lock 表示用ReentrantLock进行数据保护,速度仅次于内置锁,lock_fair是它的公平版本,不过速度上有大幅下降,变慢了将近20倍
  • sem 表示使用Semaphore也就是信号量进行数据保护,速度也不错,sem_fair是它的公平版本,和ReentrantLock上出现的情况一样,公平版本比非公平版本出现了大幅的速度下降,慢了10倍
  • CAS 用CAS操作实现简单的自旋锁,不具有排队功能
  • pure_CAS 表示直接用使用AtomicLong类型的count变量进行计数,就不需要锁保护了,速度也是非常快得

调整测试的参数将线程数改为2,每线程增量改为50000即总增量不变的情况下得到的结果如下:


case : raw
counted: 91460
target : 100000
time : 4.767ms case : syn
counted: 100000
target : 100000
time : 12.385ms case : CLH
counted: 100000
target : 100000
time : 86.12ms case : lock
counted: 100000
target : 100000
time : 33.91ms case : lock_fair
counted: 100000
target : 100000
time : 108.119ms case : sem
counted: 100000
target : 100000
time : 62.503ms case : sem_fair
counted: 100000
target : 100000
time : 83.035ms case : CAS
counted: 100000
target : 100000
time : 13.528ms case : pure_CAS
counted: 100000
target : 100000
time : 11.377ms

基本锁

ReentrantLock的非公平版本在两次测试中并没有很大的变化,比较稳定。

公平锁

在不必要的情况下不去使用锁或者信号量的公平版本,它们相比非公平版本要慢很多,尤其当竞争非常激烈时。从第二组数据来看公平锁与非公平锁的差距缩小了很多,因为在竞争程度比较低(线程数少)的时候花在维护队列上的时间将大大减少。

CAS

正如Java并发编程中提到的CAS在高竞争的环境下性能不如直接使用锁,因为当竞争非常激烈时CAS花在重试上的时间将会大量增加(因为竞争激烈变量变化越快,CAS操作失败可能性越大)。而在竞争不是非常激烈的情况下CAS的开销更小,比如在第二次此时中的CAS自旋锁时间就比直接用ReentrantLock来得少。

内置锁

虽然说在Java并发编程一书中提到JDK1.6的Lock实现比synchronized要略快,这在JDK1.7中恐怕已经不成立了。

AbstractQueuedSynchronizer

http://javarticles.com/2012/10/abstractqueuedsynchronizer-aqs.html#prettyPhoto

JAVA 并发:CLH 锁 与 AbstractQueuedSynchronizer的更多相关文章

  1. java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock

    原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...

  2. 从源码学习Java并发的锁是怎么维护内部线程队列的

    从源码学习Java并发的锁是怎么维护内部线程队列的 在上一篇文章中,凯哥对同步组件基础框架- AbstractQueuedSynchronizer(AQS)做了大概的介绍.我们知道AQS能够通过内置的 ...

  3. Java并发编程锁系列之ReentrantLock对象总结

    Java并发编程锁系列之ReentrantLock对象总结 在Java并发编程中,根据不同维度来区分锁的话,锁可以分为十五种.ReentranckLock就是其中的多个分类. 本文主要内容:重入锁理解 ...

  4. java 并发多线程 锁的分类概念介绍 多线程下篇(二)

    接下来对锁的概念再次进行深入的介绍 之前反复的提到锁,通常的理解就是,锁---互斥---同步---阻塞 其实这是常用的独占锁(排它锁)的概念,也是一种简单粗暴的解决方案 抗战电影中,经常出现为了阻止日 ...

  5. Java并发 - (无锁)篇6

    , 摘录自葛一鸣与郭超的 [Java高并发程序设计]. 本文主要介绍了死锁的概念与一些相关的基础类, 摘录自葛一鸣与郭超的 [Java高并发程序设计]. 无锁是一种乐观的策略, 它假设对资源的访问是没 ...

  6. Java并发编程锁之独占公平锁与非公平锁比较

    Java并发编程锁之独占公平锁与非公平锁比较 公平锁和非公平锁理解: 在上一篇文章中,我们知道了非公平锁.其实Java中还存在着公平锁呢.公平二字怎么理解呢?和我们现实理解是一样的.大家去排队本着先来 ...

  7. Java并发编程-再谈 AbstractQueuedSynchronizer 1 :独占模式

    关于AbstractQueuedSynchronizer JDK1.5之后引入了并发包java.util.concurrent,大大提高了Java程序的并发性能.关于java.util.concurr ...

  8. 【Java并发】详解 AbstractQueuedSynchronizer

    前言 队列同步器 AbstractQueuedSynchronizer(以下简称 AQS),是用来构建锁或者其他同步组件的基础框架.它使用一个 int 成员变量来表示同步状态,通过 CAS 操作对同步 ...

  9. Java并发——显示锁

    Java提供一系列的显示锁类,均位于java.util.concurrent.locks包中. 锁的分类: 排他锁,共享锁 排他锁又被称为独占锁,即读写互斥.写写互斥.读读互斥. Java的ReadW ...

随机推荐

  1. fetch更新本地仓库两种方式:

    来源:https://www.cnblogs.com/chenlogin/p/6592228.html //方法一 $ git fetch origin master //从远程的origin仓库的m ...

  2. VS2017新建视图中文乱码解决办法

    问题:VS2017 ASP.NET Core 新建视频默认为ASNI编码格式 解决办法 1:直接将视图页面通过记事本打开,然后另存为UTF-8解决. 2:安装扩展TextTools解决视图文件编码问题 ...

  3. Docker Compose模板文件介绍

    模板文件是使用 Compose 的核心,涉及到的指令关键字也比较多,这里面大部分指令跟 docker run 相关参数的含义都是类似的.默认的模板文件名称为 docker-compose.yml ,格 ...

  4. Java内存溢出问题总结

    使用Java那么久,在此总结一下Java中常见的内存溢出问题以及对应的解决思路 堆溢出 报错信息 java.lang.OutOfMemoryError: Java heap space 报错原因 堆中 ...

  5. 基于GTK+3 开发远程控制管理软件(C语言实现)系列二 Centos7下开发环境搭建

    一.安装gcc gcc-c++ make等编译工具 yum install gcc gcc-c++ kernel-devel 这一步,其实可以不用做,你在安装Centos7的时候,如果选择开发模式安装 ...

  6. flex布局中transform出错

    在flex布局下,若应用transform 的动画的子元素没有使用进行定位,则动画过程中,子元素将相对display:flex的元素进行static定位 动画结束后位置正常: 修复代码只需要posit ...

  7. 执行HBase shell时出现ERROR: org.apache.hadoop.hbase.ipc.ServerNotRunningYetException: Server is not running yet错误解决办法(图文详解)

    不多说,直接上干货! [kfk@bigdata-pro01 bin]$ jps NameNode ResourceManager JournalNode HMaster DataNode HRegio ...

  8. 【转】谷歌三大核心技术(三)Google BigTable中文版

      谷歌三大核心技术(三)Google BigTable中文版 摘要 Bigtable 是一个分布式的结构化数据存储系统,它被设计用来处理海量数据:通常是分布在数千台普通服务器上的PB级的数据.Goo ...

  9. NHibernate with ASP.NET MVC 入门示例

    目的:初步了解NHibernate的用法,包括数据库的CRUD, 基于ASP.NET MVC 项目模板 步骤: 创建ASP.NET MVC 新项目 使用NuGet引入FluentNHibernate ...

  10. java 写法推荐

    1. for循环 for (int i = 0; i < list.size(); i++) { int item = list.get(i); System.out.println(" ...