`

java park/unpark 【java并发】基于JUC CAS原理,自己实现简单独占锁

阅读更多
LockSupport.park();  停止
System.out.println("======"); 

为阻塞线程提供基础的功能,它由一对park和unpark组成,park会阻塞当前线程,unpark“唤醒”等待线程;内部使用了类似信号量的“许可”机制,该许可为0,park会在许可等于0的时候下阻塞,等于1的时候立即返回,并且将许可减为0,umpark会尝试唤醒线程,并且将许可+1(最大值就是1)。因此,如果先调用unpark方法,再调用park是无效的


  unpark /patk 成对出现
synchronized的基本原理回顾
在jvm内部,所有对象都含有单一的锁,jvm负责跟踪监视被加锁次数,叫做对象监视器。当线程第一次给对象加锁的时候,计数器会加1,离开时会减1.同样任务是可重入的,每次重入也是加1,离开减1. 
synchronized是独占式的,拿到对象锁才能继续,没有获取到锁就会阻塞。
JUC CAS乐观锁基本原理
synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
乐观锁就是假设没有冲突而去完成某项操作,如果因为冲突失败就重试。
原理有点类似于在数据库记录字段增加一个版本号,每次更新的时候做两个事情:
1.检查数据库记录当前的版本号和读取到的版本号是否一致,不一致说明数据已不是最新,更新失败需要重试.
2.如果版本号一致,更新成功,同时将版本号加1.
在jdk1.5开始,Doug Lea在JUC类库里提供了类似的乐观锁的机制叫CAS.
CAS简介:compareAndSet的意思,就是先比较是否是期望值(是期望值,说明没人更改过,当然也有可能有ABA情况),如果是再设值,不是就设值失败,线程阻塞。如果是基于这个,就要保证比较和设值这两个动作是原子性的,如何保证呢?这个是借助于JNI,利用CPU硬件支持来完成的。利用硬件提供swap和test_and_set指令,单CPU下同一指令的多个指令周期不可中断,SMP中通过锁总线支持这两个指令的原子性。

volilate关键字
Java语言规范允许线程保存共享成员变量的私有拷贝,当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样就延伸出可见性的一个问题。
CAS解决了比较和更新的原子性,但是还有另外一个问题就是要保证可见性。Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
volatile关键字有两层含义:
1.对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
2.volatile前后的代码不能重排

其他话题
volilate和cas只能乐观锁保证的状态控制的正确,而在设置状态失败的时候,仍然需要阻塞线程。juc里提供了LockSupport的park和unpark方法用于阻塞线程。而不同的场景下需要不同的等待策略和锁共享策略,juc提供了AbstractQueuedSynchronizer(AQS)为基类的一序列不同的锁,底层都是基于CAS、LocakSupport和Queue来管理,后续有时间细细分析。

juc基于的CAS,提供了带有原子性的基本类型封装类,如AtomicInteger、AtomicLong等。
AtomicInteger原理:
如自增:
Java代码 
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/ 
public final int getAndIncrement() { 
    for (;;) { 
        int current = get(); 
        int next = current + 1; 
        if (compareAndSet(current, next))  //cas 
            return current; 
    } 

compareAndSet的实现如下:
Java代码 
public final boolean compareAndSet(int expect, int update) { 
urn unsafe.compareAndSwapInt(this, valueOffset, expect, update); //JNI调用 




自己实现简单乐观独占锁
基于cas,本人简单实现了一个乐观独占锁,代码如下:

基于CAS简单乐观独占锁

Java代码 
package lock.test; 
 
import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.atomic.AtomicBoolean; 
import java.util.concurrent.locks.LockSupport; 
 
/**
* 简单乐观独占锁
*/ 
public class OptimisticExclusiveLock { 
 
    /**
     * 独占锁标记 true 锁不可用 false 锁可用
     */ 
    private AtomicBoolean state = new AtomicBoolean(false); 
    List<Thread>          queue = new ArrayList<Thread>();//阻塞队列 
 
    public boolean lock() { 
        if (!state.get()&&state.compareAndSet(false, true)) {//取锁成功不会阻塞,程序会继续执行 
            return true; // 利用CAS 
        } else { 
            queue.add(Thread.currentThread());//加入阻塞队列 
            LockSupport.park();//阻塞线程 
            return false; 
        } 
    } 
 
    public boolean unLock() { 
        if (state.get()) { 
            queue.remove(Thread.currentThread());//从队列里移除 
            if (state.compareAndSet(true, false)) {// 利用CAS 
                if(!queue.isEmpty()){ 
                    LockSupport.unpark(queue.get(0));//唤醒第一个等待线程 
                } 
                return true; 
            } 
            return false; 
        } else { 
            return false; 
        } 
    } 


简单乐观独占锁测试

Java代码 
package lock.test; 
 
/**
* @author Administrator 独占锁测试
*/ 
public class OptimisticExclusiveLockTest { 
 
    public static OptimisticExclusiveLock lock = new OptimisticExclusiveLock(); // 独占锁 
    public static volatile int            i    = 0;                            // 保证可见性 
 
    public class Task implements Runnable { 
 
        @Override 
        public void run() { 
            while (true) { 
                try { 
                    lock.lock();//加锁 
                    i += 2; 
                    System.out.println("thread name:" + Thread.currentThread().getName() + " i=" + i); 
                } finally { 
                    lock.unLock();//释放锁 
                    try { 
                        Thread.currentThread().sleep(10); 
                    } catch (InterruptedException e) { 
                        // TODO Auto-generated catch block 
                        e.printStackTrace(); 
                    } 
                } 
            } 
        } 
    } 
 
    public void runTask() { 
        for (int i = 0; i < 100; i++) { 
            Thread t = new Thread(new Task(), "thread" + i); 
            t.start(); 
        } 
    } 
 
    public static void main(String[] args) { 
        OptimisticExclusiveLockTest test = new OptimisticExclusiveLockTest(); 
        test.runTask(); 
 
    } 


这里实现的简单乐观独占锁很简单,但是能保证并发性。
JUC里面基于CAS实现了很多的锁,主要是基于AQS实现,如ReentrantLock,CountDownLatch,Semaphore,FutureTask等,适用于不同的锁场景。


     
分享到:
评论
1 楼 lyaqys 2014-12-30  
lz实现的OptimisticExclusiveLock有点问题哦,
1. List<Thread>  queue = new ArrayList<Thread>();//阻塞队列  不是线程安全的
2. The park method may also return at any other time, for "no reason", so in general must be invoked within a loop that rechecks conditions upon return. In this sense park serves as an optimization of a "busy wait" that does not waste as much time spinning, but must be paired with an unpark to be effective.----park可能无缘无故return,因此要用while包起来。

可以参看LockSupport源码里面给的例子
class FIFOMutex {
   private final AtomicBoolean locked = new AtomicBoolean(false);
   private final Queue<Thread> waiters
     = new ConcurrentLinkedQueue<Thread>();

   public void lock() {
     boolean wasInterrupted = false;
     Thread current = Thread.currentThread();
     waiters.add(current);

     // Block while not first in queue or cannot acquire lock
     while (waiters.peek() != current ||
            !locked.compareAndSet(false, true)) {
        LockSupport.park(this);
        if (Thread.interrupted()) // ignore interrupts while waiting
          wasInterrupted = true;
     }

     waiters.remove();
     if (wasInterrupted)          // reassert interrupt status on exit
        current.interrupt();
   }

   public void unlock() {
     locked.set(false);
     LockSupport.unpark(waiters.peek());
   }
}

相关推荐

    尚硅谷大厂面试题第三季周阳主讲

    【标题】"尚硅谷大厂面试题第三季周阳主讲"主要涵盖了Java后端面试中的核心知识点,包括Java并发编程(JUC)、Redis缓存系统以及Spring框架的应用。本内容整理了B站尚硅谷频道关于大厂面试的全部资料,帮助考生全面...

    kernel-devel-4.18.0-553.45.1.el8-10.x86-64.rpm

    Rocky Linux 8.10内核包

    Simulink中三阶单环多位量化Σ-Δ调制器的设计与实现-音频带ADC的应用(复现论文或解答问题,含详细可运行代码及解释)

    内容概要:本文档详细介绍了如何在Simulink中设计一个满足特定规格的音频带ADC(模数转换器)。首先选择了三阶单环多位量化Σ-Δ调制器作为设计方案,因为这种结构能在音频带宽内提供高噪声整形效果,并且多位量化可以降低量化噪声。接着,文档展示了具体的Simulink建模步骤,包括创建模型、添加各个组件如积分器、量化器、DAC反馈以及连接它们。此外,还进行了参数设计与计算,特别是过采样率和信噪比的估算,并引入了动态元件匹配技术来减少DAC的非线性误差。性能验证部分则通过理想和非理想的仿真实验评估了系统的稳定性和各项指标,最终证明所设计的ADC能够达到预期的技术标准。 适用人群:电子工程专业学生、从事数据转换器研究或开发的技术人员。 使用场景及目标:适用于希望深入了解Σ-Δ调制器的工作原理及其在音频带ADC应用中的具体实现方法的人群。目标是掌握如何利用MATLAB/Simulink工具进行复杂电路的设计与仿真。 其他说明:文中提供了详细的Matlab代码片段用于指导读者完成整个设计流程,同时附带了一些辅助函数帮助分析仿真结果。

    计算机课后习题.docx### 【计算机科学】研究生入学考试计算机组成原理专项题库设计:考研复习资源集成与优化

    内容概要:该题库专为研究生入学考试计算机组成原理科目设计,涵盖名校考研真题、经典教材课后习题、章节题库和模拟试题四大核心模块。名校考研真题精选多所知名高校的计算机组成原理科目及计算机联考真题,并提供详尽解析,帮助考生把握考研命题趋势与难度。经典教材课后习题包括白中英《计算机组成原理》(第5版)和唐朔飞《计算机组成原理》(第2版)的全部课后习题解答,这两部教材被众多名校列为考研指定参考书目。章节题库精选代表性考题,注重基础知识与重难点内容,帮助考生全面掌握考试大纲要求的知识点。模拟试题依据历年考研真题命题规律和热门考点,精心编制两套全真模拟试题,并附标准答案,帮助考生检验学习成果,评估应试能力。 适用人群:计划参加研究生入学考试并报考计算机组成原理科目的考生,尤其是需要系统复习和强化训练的学生。 使用场景及目标:①通过研读名校考研真题,考生可以准确把握考研命题趋势与难度,有效评估复习成效;②通过经典教材课后习题的练习,考生可以巩固基础知识,掌握解题技巧;③通过章节题库的系统练习,考生可以全面掌握考试大纲要求的各个知识点,为备考打下坚实基础;④通过模拟试题的测试,考生可以检验学习成果,评估应试能力,为正式考试做好充分准备。 其他说明:该题库不仅提供详细的题目解析,还涵盖了计算机组成原理的各个方面,包括计算机系统概述、数据表示与运算、存储器分层、指令系统、中央处理器、总线系统和输入输出系统等。考生在使用过程中应结合理论学习与实践操作,注重理解与应用,以提高应试能力和专业知识水平。

    __UNI__DB9970A__20250328141034.apk.1

    __UNI__DB9970A__20250328141034.apk.1

    minio-rsc-Rust资源

    rust for minio

    4-4-台区智能融合终端功能模块型式规范(试行).pdf

    国网台区终端最新规范

    《基于YOLOv8的化工管道焊缝缺陷检测系统》(包含源码、可视化界面、完整数据集、部署教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计.zip

    资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。

    python源码-1个机器学习相关资源

    一个简单的机器学习代码示例,使用的是经典的鸢尾花(Iris)数据集,通过 Scikit-learn 库实现了一个简单的分类模型。这个代码可以帮助你入门机器学习中的分类任务。

    pyqt离线包,pyqt-tools离线包

    pyqt离线包,pyqt-tools离线包

    《基于YOLOv8的船舶机舱灭火系统状态监测系统》(包含源码、可视化界面、完整数据集、部署教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计.zip

    资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。

    SQL常用日期和时间函数整理及使用示例

    SQL常用日期和时间函数整理及在sqlserver测试示例 主要包括 1.查询当前日期GETDATE 2.日期时间加减函数DATEADD 3 返回两个日期中指定的日期部分之间的差值DATEDIFF 4.日期格式转换CONVERT(VARCHAR(10),GETDATE(),120) 5.返回指定日期的年份数值 6.返回指定日期的月份数值 7.返回指定日期的天数数值

    GSDML-V2.3-Turck-BL20-E-GW-EN-20160524-010300.xml

    GSDML-V2.3-Turck-BL20_E_GW_EN-20160524-010300.xml

    T_CPCIF 0225-2022 多聚甲醛.docx

    T_CPCIF 0225-2022 多聚甲醛.docx

    《基于YOLOv8的智能仓储货物堆码倾斜预警系统》(包含源码、可视化界面、完整数据集、部署教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计.zip

    《基于YOLOv8的智能仓储货物堆码倾斜预警系统》(包含源码、可视化界面、完整数据集、部署教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计

    蚕豆脱壳机设计.zip

    蚕豆脱壳机设计.zip

    附件2-2:台区智能融合终端入网专业检测单位授权委托书.docx

    台区终端电科院送检文档

    Y6一39一No23.6D离心通风机 CAD().zip

    Y6一39一No23.6D离心通风机 CAD().zip

    django自建博客app

    django自建博客app

    附件3-4:台区智能融合终端全性能试验增值税发票开具确认单.docx

    台区终端电科院送检文档

Global site tag (gtag.js) - Google Analytics