理解AQS(AbstractQueuedSynchronizer)

可重入锁又名递归锁:是指在同一线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。

Java中ReentrantLock和synchranized都是可重入锁,可重入锁得一个优点是可一定程度避免死锁

synchronized的重入的实现原理:

每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。

在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。

当执行monitorexit时,java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。

唤醒线程的三种方式:

Object类中的wait、notify、notifyAll用于线程等待和环形的方法,都必须在synchronized内部执行(必须用到关键字synchronized)

复制代码
public class LockSupportDemo
{
    static Object objectlock=new Object();
    public static void main(String[] args)
    {
            new Thread(()->{
                synchronized (objectlock){
                    System.out.println(Thread.currentThread().getName()+"\t"+"--------come in");
                    try {
                        objectlock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"\t"+"--------被唤醒");
                }
                },"A").start();

                new Thread(()->{
                    synchronized (objectlock)
                    {
                        objectlock.notify();
                        System.out.println(Thread.currentThread().getName()+"\t"+"--------通知");
                    }
                    },"B").start();
    }
}
复制代码

传统的synchronized和Lock实现等待唤醒通知的约束,线程先要获得并持有锁,必须在块(synchronized或lock)中,必须要先等待后唤醒,线程才能够被唤醒

复制代码
 public static void lockAwaitSignal() {
        new Thread(()->{
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"\t"+"--------come in");
                try {
                    condition.await();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"\t"+"--------被唤醒");
            }finally {
                lock.unlock();
            }
            },"A").start();

        new Thread(()->{
            lock.lock();
            try {
                condition.signal();
                System.out.println(Thread.currentThread().getName()+"\t"+"--------通知");
            }finally {
                lock.unlock();
            }
            },"B").start();
    }
复制代码

 

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。

LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对象的唤醒方法。归根结底,LockSupport调用的Unsafe中的native代码。

LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程

LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1,0的开关,默认是0,调用一次unpack就加1变成1,调用一个park会消费permit,也就是将1变成0,同时park立即返回,如再次调用park会变成阻塞(因为permit为零了会阻塞在这里,一直到permit变为1),这时调用unpark会把permit置为1,每个线程都有一个相关的permit,permit最多只有一个,重复调用unpark也不会积累凭证

AQS是用来构建锁(ReentrantLock)或者其它同步器组件(semaphore、countdownlatch等)的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类型变量表示持有锁的状态

posted @   渺小的不能再渺小了  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示