AQS、LockSupport、Lock和ReentrantLock、Condition

AbstractQueuedSynchronized

    AbstractQueuedSynchronized(AQS)是用来构建锁和其他同步组件的基础框架。它使用了一个int成员变量state来表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
    同步器的主要使用方式是继承,子类同步器在实现AQS时只需要实现共享资源state的获取与释放方式,至于具体线程等待队列的维护AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:

  • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
  • tryAcquire(int):独占方式。尝试获取资源
  • tryRelease(int):独占方式。尝试释放资源
  • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
  • tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

    AQS定义了两种资源共享方式:Exclusive(独占)和Share(共享),不同的自定义同步器争用共享资源的方式也不同。

public abstract class AbstractQueuedSynchronizer
       extends AbstractOwnableSynchronizer
       implements java.io.Serializable {
   static final class Node {
   	   //标记节点在共享式模式下等待
       static final Node SHARED = new Node();
       //标记节点在独占式模式下等待
       static final Node EXCLUSIVE = null;
       //线程已经被取消获取共享状态
       static final int CANCELLED = 1;
       //后继节点需要被唤醒
       static final int SIGNAL = -1;
       //节点在condition上等待
       static final int CONDITION = -2;
       //下一个共享式节点需要被无条件传递
       static final int PROPAGATE = -3;
       volatile int waitStatus;
       volatile Node prev;
       volatile Node next;
       //拥有锁的线程
       volatile Thread thread;
       Node nextWaiter;
   }

   private transient volatile Node head;
   private transient volatile Node tail;
   //负责管理线程状态,子类主要通过对state的操作来决定是否获取锁和是否进入队列。
   private volatile int state;
}

LockSupport

    LockSuppor是构建同步组件的基础工具,它提供了最基本的线程阻塞和唤醒的公共静态方法。

  • park():阻塞当前线程,只有调用unpark(thread)或者当前线程中断才会返回。
  • unpark(Thread thread):唤醒线程。

Lock和ReentrantLock

    JDK1.5后新增的显示锁,可以定时、轮询和中断,如果不需要这些特性尽量选择synchronized。Lock必须保证正常的加锁和解锁,否则会抛出异常。
    ReentrantLock可重入锁,默认的确是非公平锁也可以通过构造方法设置为公平锁。ReentrantLock是基于AQS实现的,它的初始state为0表示未锁定状态也只有这个状态锁才是可用状态,当有线程得到锁时state+1,如果同一个线程多次获取锁时state累加,但是每获取一次之后都要释放一次直到state为0的时候锁才释放。

简单使用

public class MyThread extends Thread {
   Lock lock = new ReentrantLock();

   @Override
   public void run() {
       //如果加锁出现异常就不会执行unlock()
       lock.lock();
       try {
           System.out.println("MyThread");
       } catch (Exception e) {
           e.printStackTrace();
       } finally {
           //保证如果加上了锁,就一定会解锁
           lock.unlock();
       }
   }
}

原理分析

在这里插入图片描述

lock

    final void lock() {
       //修改锁的状态为已占有
       if (compareAndSetState(0, 1))
           //成功之后就把获得锁的线程设置为自己
           setExclusiveOwnerThread(Thread.currentThread());
       else
           //尝试获取锁
           acquire(1);
   }
   
   public final void acquire(int arg) {
       /**
        *调用实现类的尝试获取锁的方法,成功之后直接返回
        * 公平锁和非公平锁的区别就在这个方法
        */
       if (tryAcquire(arg)) {
           return;
       }
       //获取锁失败之后,入队当前线程
       Node node = addWaiter(Node.EXCLUSIVE);
       //根据公平性的原则为队列的线程获取锁
       if (acquireQueued(node, arg)) {
           //让当前线程产生中断
           selfInterrupt();
       }
   }

公平锁和非公平锁tryAcquire

    protected final boolean tryAcquire(int acquires) {
       final Thread current = Thread.currentThread();
       int c = getState();
       // c=0 意味着锁是可以占用的状态
       if (c == 0) {
           /**
            * 公平锁
            * 如果当前线程是队列的第一个线程,就获取锁,
            * 设置锁的状态,和占用锁的线程
            */
           if (!hasQueuedPredecessors() &&
                   compareAndSetState(0, acquires)) {
               setExclusiveOwnerThread(current);
               return true;
           }
           /**
            * 非公平锁
            * 不用查询当前线程是否是队列的第一个线程
            */
           if (compareAndSetState(0, acquires)) {
               setExclusiveOwnerThread(current);
               return true;
           }
       } else if (current == getExclusiveOwnerThread()) {
           //当前线程拥有锁,由于可重入性,可以使用并且更新锁的状态
           int nextc = c + acquires;
           if (nextc < 0)
               throw new Error("Maximum lock count exceeded");
           setState(nextc);
           return true;
       }
       return false;
   }

和synchronized的区别

  • synchronized为Java的一个关键字;Lock是一个接口。
  • synchronized会自动释放锁,是隐式锁;Lock手动开启和关闭锁,是显示锁
  • synchronized时非公平的可重入锁;Lock是可重入锁默认为非公平锁,但是可以修改。
  • synchronized不可以中断;Lock支持定时、轮询和中断。
  • synchronized无法获取锁的状态;Lock可以得到是否获取锁。
  • synchronized有代码块和方法锁;Lock只有代码锁。

Condition

    提供了类似对象监视器的方法。
    Condition更强大的地方是可以精细的控制多线程的休眠与唤醒。对于同一个锁可以创建多个Condition,在不同的情况下使用不同的Condition。

简单使用

public class MyThread extends Thread {
   Lock lock = new ReentrantLock();
   Condition condition = lock.newCondition();
   boolean flag = false;

   @Override
   public void run() {
       //如果加锁出现异常就不会执行unlock()
       lock.lock();
       try {
           while (flag) {
               //等待
               condition.await();
           }
           System.out.println("MyThread");
           flag = false;
           //类似于notify()
           condition.signalAll();
       } catch (Exception e) {
           e.printStackTrace();
       } finally {
           //保证如果加上了锁,就一定会解锁
           lock.unlock();
       }
   }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值