java中lock锁使用以及AQS理解

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_40921797/article/details/84502902

lock锁

和关键字synchronized内建锁不同,Lock锁是完全由java语言实现的,Lock锁的代码在Java.util包下来完成我们同步互斥的访问临界资源。synchronized锁会使得其他线程阻塞等待等待被唤醒,而Lock使得其他请求访问的线程自旋等待竞争锁。Lock体系拥有可中断的获取锁以及超时获取锁以及共享锁等内建锁不具备的特性。
lock锁,使用方法

lock lock = new ReentrantLock();
try{    
lock.lock();    
以下代码只能一个线程可以运行
}finally{    
lock.unlock();
}

Lock是接口,所以不能自己实例化对象,要通过子类实现接口来实例化出真正可以使用的锁,如可重入锁ReentrantLock来实例化对象。我们看一个ReentrantLock中的方法

public ReentrantLock() {
    sync = new NonfairSync();
}

如果多看几个我们就会发现ReentrantLock中的所有方法实际上都是调用的其类中的静态内部类Sync中的方法实现的,abstract static class Sync extends AbstractQueuedSynchronizer{ ……} Sync又是继承了AbstractQueuedSynchronizer(简称AQS同步器)lock和AQS向一层封装。lock是设计出来面向使用者的,它定义了和用户交互所需要的接口。而AQS面向的是所的使用者,AQS同步器是用来构建锁以及其他同步组件的基础框架,它简化了锁的实现,并且实现了同步状态管理、线程排队、线程等待唤醒等底层操作,并对上层屏蔽。
在这里插入图片描述

AQS同步器

AQS同步器是用来构建锁以及其他同步组件的基础框架。是所有JUC锁实现的基础类。而它的主要实现依赖于一个int状态的变量和一个先入先出的队列构成的同步队列。子类Sync们主要通过重写AQS中几个被pritected修饰尝试获取锁释放锁的方法来改变同步状态。
在这里插入图片描述
AQS采用模板方法模式
AQS是一个抽象类规定了核心算法并封装在抽象类中,子类只能用不能改比如:enq方法初始化队列、CompareAndSetState()CAS比较交换机制等,具体的操作比如:tryAcquire尝试获取锁,延迟到子类实现。这是典型的模板方法模式。
我们也可以通过封装一个内部静态类继承AQS实现自己的Lock锁。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

class MyLock implements Lock {
    //-------------------------AQS↓
    static class Sync extends AbstractQueuedSynchronizer{
        @Override
        protected boolean tryAcquire(int arg) {
            if(arg!=1){throw new RuntimeException("参数不为1");}
            if(compareAndSetState(0,arg)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if(getState()==0){
                //状态已经为0,不需要释放
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        @Override
        protected boolean isHeldExclusively() {
            return getState()==1;
        }
    }
    //_________________AQS↑
    Sync sync = new Sync();
    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.isHeldExclusively();
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}
public class Test{
    private static Lock lock = new MyLock();
    public static void main(String[] args) {
        for(int i=0;i<10;i++){
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        lock.lock();
                        System.out.println("锁住了"+Thread.currentThread().getName());
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        System.out.println("锁要释放了哦"+Thread.currentThread().getName());
                        lock.unlock();
                    }
                }
            });
            thread.start();
        }
    }
}

我们已经通过使用sync继承AQS我们再通过自己的Lock封装来实现获取锁,释放锁互斥访问代码块的操作。那么让我们深入理解以下AQS吧

理解AQS

独占锁的释放和获取acquire,release、同步锁的获取与释放acquireShare,releaseShared、响应中断锁acquireInterruptibly、超时锁acquireNanos等一系列JCU锁都依赖于AQS提供的模板方法。

同步队列

在AQS中有一个静态内部类Node,AQS将每一个尝试获取锁而不成功的线程包装成一个Node姐节点进入同步队列中,自旋的去竞争锁。

static final class Node {
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;

//节点状态值:初始状态为0
static final int CANCELLED = 1;//当前节点从同步队列中取消
static final int SIGNAL = -1;//当前节点的后继节点出于阻塞状态,但是依然在同步队列中,如果当前节点释放同步状态会通知后继节点继续运行。
static final int CONDITION = -2;//表示当前节点处于等待队列中。如果被唤醒会从等待队列移动之同步队列中。
static final int PROPAGATE = -3;//与共享模式相关,在共享模式中,该状态标识结点的线程处于可运行状态,后继acquireShared结点可以直接获取锁执行

volatile int waitStatus; //节点状态,初始状态为0
volatile Node prev;//同步队列前驱节点
volatile Node next;//同步队列后继节点
volatile Thread thread;//当前节点包装的线程对象
Node nextWaiter;//等待队列中的下一个节点
……
}
展开阅读全文

没有更多推荐了,返回首页