可重入锁和非可重入锁

    广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者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<Thread> owner = new AtomicReference<Thread>();//记录当前锁的持有线程对象

    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<Thread> owner = new AtomicReference<Thread>();//记录当前锁的持有线程对象
    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;// 失败
}

猜你喜欢

转载自my.oschina.net/u/3858564/blog/2880489