博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
可重入锁和非可重入锁
阅读量:6095 次
发布时间:2019-06-20

本文共 3542 字,大约阅读时间需要 11 分钟。

hot3.png

    广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。

可重入锁:

    ReentrantLock和synchronized都是可重入锁,下面是一个用synchronized实现的例子:

public class ReentrantTest implements Runnable {

    public synchronized void get() {

        System.out.println(Thread.currentThread().getName());
        set();
    }

    public synchronized void set() {

        System.out.println(Thread.currentThread().getName());
    }

    public void run() {

        get();
    }

    public static void main(String[] args) {

        ReentrantTest rt = new ReentrantTest();
        for(;;){
            new Thread(rt).start();
        }
    }
}

    整个过程没有发生死锁的情况,截取一部分输出结果如下: 

Thread-8492

Thread-8492
Thread-8494
Thread-8494
Thread-8495
Thread-8495
Thread-8493
Thread-8493

    set()和get()同时输出了线程名称,表明即使递归使用synchronized也没有发生死锁,证明其是可重入的。

 

不可重入锁

    不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁。

自定义模拟不可重入锁:

    一个经典的讲解,使用自旋锁来模拟一个不可重入锁,代码如下:

package com.thread;import java.util.concurrent.atomic.AtomicReference;public class UnreentrantLock {    private AtomicReference
owner = new AtomicReference
();//记录当前锁的持有线程对象 public void lock() {//加锁 Thread current = Thread.currentThread();//获取当前线程对象 for (; ; ) {//自旋(被当前线程或其他线程持有锁,就会循环) if (owner.compareAndSet(null, current)) {//只有锁可用即为null,才能设置当前线程为锁持有对象,并返回true return; } } } public void unlock() {//解锁 Thread current = Thread.currentThread();//获取当前线程对象 owner.compareAndSet(current, null);//设置锁的持有对象为null }}

    使用原子引用来存放线程,同一线程两次调用lock()方法,如果不执行unlock()释放锁的话,第二次调用自旋的时候就会产生死锁,这个锁就不是可重入的。

自定义模拟可重入锁:

    实际上同一个线程不必每次都去释放锁再来获取锁,这样的调度切换是很耗资源的。稍微改一下,把它变成一个可重入锁:

package com.thread;import java.util.concurrent.atomic.AtomicReference;public class UnreentrantLock {    private AtomicReference
owner = new AtomicReference
();//记录当前锁的持有线程对象 private int state = 0;//记录重入次数 public void lock() {//加锁 Thread current = Thread.currentThread();//获取当前线程对象 if (owner.compareAndSet(null, current)) {//当前锁可用 state = 1;//状态置为1 return; } else { if (current == owner.get()) {//如果当前线程持有锁 state++;//重入次数加1 return; } for (; ; ) {//被其他线程持有就会继续循环 if (owner.compareAndSet(null, current)) {//只有锁可用即为null,才能设置当前线程为锁持有对象,并返回true return; } } } } public void unlock() {//解锁 Thread current = Thread.currentThread();//获取当前线程对象 if (current == owner.get()) {//如果当前线程持有锁 if (state > 0) {//重入次数大于0 state--;//重入次数减1 } else { owner.compareAndSet(current, null);//设置锁的持有对象为null } } }}

    在执行每次操作之前,判断当前锁持有者是否是当前对象,采用state计数,不用每次去释放锁。

ReentrantLock中非公平的可重入锁实现:

// 非公平方式获取锁,用于tryLock()方法final boolean nonfairTryAcquire(int acquires) {   //当前线程   final Thread current = Thread.currentThread();   // 继承至AbstractQueuedSynchronizer的方法   int c = getState();//获取锁状态值   //没有线程正在竞争该锁   if (c == 0) {      // 继承至AbstractQueuedSynchronizer的方法      if (compareAndSetState(0, acquires)) {//若state为0则将state修改为acquires的值,状态0表示锁没有被占用         setExclusiveOwnerThread(current);// 设置当前线程独占         return true;// 成功      }   } else if (current == getExclusiveOwnerThread()) {// 当前线程拥有该锁      int nextc = c + acquires;// 增加重入次数      if (nextc < 0) // overflow(计数值小于0,则抛出异常)         throw new Error("Maximum lock count exceeded");      // 继承至AbstractQueuedSynchronizer的方法      setState(nextc);//设置锁状态值      return true;// 成功   }   return false;// 失败}

转载于:https://my.oschina.net/langwanghuangshifu/blog/2880489

你可能感兴趣的文章
JAVA GC
查看>>
codeforce 599B Spongebob and Joke
查看>>
3springboot:springboot配置文件(外部配置加载顺序、自动配置原理,@Conditional)
查看>>
9、Dubbo-配置(4)
查看>>
前端第七天
查看>>
BZOJ 2190[SDOI2008]仪仗队
查看>>
图解SSH原理及两种登录方法
查看>>
[转载] 七龙珠第一部——第058话 魔境圣地
查看>>
【总结整理】JQuery基础学习---样式篇
查看>>
查询个人站点的文章、分类和标签查询
查看>>
基础知识:数字、字符串、列表 的类型及内置方法
查看>>
JSP的隐式对象
查看>>
P127、面试题20:顺时针打印矩阵
查看>>
JS图片跟着鼠标跑效果
查看>>
[SCOI2005][BZOJ 1084]最大子矩阵
查看>>
学习笔记之Data Visualization
查看>>
Leetcode 3. Longest Substring Without Repeating Characters
查看>>
USB2.0学习笔记连载(十八):keil实现寄存器的配置及相关函数讲解(二)
查看>>
SqlServer表名称定义
查看>>
jquery操作select(取值,设置选中)
查看>>