IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 利用AQS自定义XXX -> 正文阅读

[Java知识库]利用AQS自定义XXX

原文指路

AQS概述

  • AbstractQueuedSynchronizer(抽象队列式同步器)
  • 多线程访问共享资源的同步器框架
  • AQS是实现锁的关键
锁面向使用者,AQS面向锁实现者

AQS底层结构

在这里插入图片描述

1.state

  • AQS内部用volatile修饰的int类型的成员变量state控制同步状态
state=0:表示没有线程正在独占共享资源的锁
state=1:表示有线程正在共享资源的锁
  • state既可以表示锁占没占,也可以表示占多少
1.占没占
	独占锁state两个状态01表示占没占
	读写锁32位数拆成前后16位分别表示两把锁
2.占多少
	state数字量
  • AQS自带方法
getState():获取当前的同步状态
setState():设置当前同步状态,非安全
compareAndSetState():使用CAS设置状态,保证状态设置的原子性

2.CLH队列

  • 虚拟双端队列(不存在队列实例,只存在节点间的关联关系)
  • AQS内部将请求共享资源的线程包装成CLH锁队列的一个节点Node实现锁分配

3.模板模式

  • 工作的流程固定,但具体实现不固定
  • 比如:JdbcTemplate、HttpServlet
1.JDBC中获取数据库连接等流程是固定不变的,只是sql语句不同,返回结果包装成什么对象不确定
	JdbcTemplate将其固定流程封装,只需用户编写sql以及返回部分
	--------------------------------------------------
	@Override
	public Account findAccount(Integer accountid) {
	    Account account = this.jdbcTemplate.queryForObject("select * from account where accountid=" + accountid, (rs, rowNum) -> {
	        Account newAccount = new Account();
	        newAccount.setAccountid(rs.getInt(1));
	        newAccount.setBalance(rs.getDouble(2));
	        return newAccount;
	    });
	    return (Account) account;
	}
2.HttpServlet的调用流程是 构造->init->service->doGet/doPost..
	service中判断具体调用doGet/doPost..哪个方法
	而doGet/doPost..需要自己实现
  • AQS中模板方法
1.独占式——获取
	acquire(int arg)
	acquireInterruptibly(int arg):响应中断
	boolean tryAcquireNanos(int arg, long nanosTimeout):尝试获取,有超时设置
2.独占式——释放
	release()
---------------------------------------------
1.共享式——获取
	acquireShared(int arg)
	acquireSharedInterruptibly(int arg):响应中断
	boolean tryAcquireSharedNanos(int arg, long nanosTimeout):尝试获取,有超时设置
2.共享式——释放
	releaseShared()
  • AQS中子类覆盖的流程方法
1.独占式
	tryAcquire(int):尝试获取资源
		成功则返回true,失败则返回false
	tryRelease(int):尝试释放资源
		成功则返回true,失败则返回false
2.共享式
	tryAcquireShared(int):尝试获取资源
		负数表示失败
		0表示成功,且没有剩余可用资源
		正数表示成功,且有剩余资源
	tryReleaseShared(int):尝试释放资源
		释放后允许唤醒后续等待结点返回true,否则返回false
3.同步器是否处于独占模式
	isHeldExclusively()
			
一般情况,只需实现共享或独占式的一种即可
但AQS也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock
  • AQS模板方法与流程方法源码(独占)
public final void acquire(int arg) {
	//tryAcquire需子类具体实现取锁条件,false表示取锁失败,true直接结束if,直接获取到锁
	if (!tryAcquire(arg) &&
		//addWaiter:创建Node加入等待队列
		//acquireQueued:并发下在当前节点加入队列的过程中可能队列中的节点取到锁走了,此时自旋判断如果当前节点在对头且可以拿到锁就返回false,跳出if,获取到锁走人,否则返回true进入if语句
		acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
		//selfInterrupt:中断线程
	    selfInterrupt();
	}
}
-----------------------------------------
public final boolean release(int arg) {
	//tryAcquire需子类具体实放锁条件,true表示能释放
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
  • AQS模板方法与流程方法源码(共享)
public final void acquireShared(int arg) {
	//tryAcquireShared需子类具体实现取锁条件,负数表示获取失败,0或正数表示获取成功且数值表示剩余资源数量
    if (tryAcquireShared(arg) < 0)
    	//doAcquireShared:操作队列
        doAcquireShared(arg);
}
-----------------------------------------
public final boolean releaseShared(int arg) {
	//tryReleaseShared需子类具体实放锁条件,true表示能释放
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

自定义CountDownLatch(共享锁)

/**
 - 自定义CountDownLatch
 - 1.state:初始值 --- MyCountDownLatch(state)模拟CountDownLatch的有参构造
 - 2.await:线程加入阻塞队列,等待state=0唤醒获取锁 -- tryAcquireShared
 - 3.countDown:state-1,state>0时没有达到取锁条件,需将锁释放,state=0时不需要释放锁 -- tryReleaseShared
 - 4.MySync:AQS实现类,重写获取释放锁的条件
 - tryAcquireShared():state=0时获取到锁返回1,否则加入阻塞队列返回-1
 - tryReleaseShared():state-1,防止多线程争抢state->自旋+CAS
 */
public class MyCountDownLatch {
    //AQS
    private static final class MySync extends AbstractQueuedSynchronizer {

        private static final long serialVersionUID = 8055037635125704937L;

        public MySync(int count) {
            //setState是AQS自带方法,设置state值
            setState(count);
        }

        //获取state值
        public int getCount() {
            return getState();
        }

        //尝试获取锁
        @Override
        protected int tryAcquireShared(int arg) {
            //state>0 都不能持有锁,都加入阻塞队列,直到state=0
            //开始时,state被初始化>0,所以此时所有的线程都不能获取锁,直到countDown全部调用完毕

            //查看底层
            // 返回值<0,state!=0,表示当前线程需处于阻塞状态,接下来acquire继续向下,将当前线程加入阻塞队列 --> 锁被占用
            // 返回值>0,state=0,表示当前当前阻塞队列中的线程同时全部唤醒,争抢释放的锁 --> 锁被释放
            return (getState() == 0) ? 1 : -1;
        }

        //尝试释放锁
        @Override
        protected boolean tryReleaseShared(int arg) {
            //返回true表示doReleaseShared --> 释放锁

            //考虑多线程共享,并发操作要保证state共享变量的原子性-->自旋+CAS
            for (; ; ) {
                //原值state
                int c = getState();
                if (c == 0) {//state=0 表示锁已经被获取,不需要释放
                    return false;
                }
                //修改state
                int nextc = c - 1;
                //compareAndSetState是AQS中的方法
                if (compareAndSetState(c, nextc)) {
                    //state设置完后为0,需要释放锁供所有线程被唤醒来争抢锁
                    return nextc == 0;
                }
            }
        }
    }

    private final MySync mySync;

    public MyCountDownLatch(int count) {
        this.mySync = new MySync(count);
    }

    public void await() {//state=0才能获取锁,否则加入阻塞队列
        mySync.acquireShared(1);
    }

    public void countDown() {//修改后state=0才能释放锁
        mySync.releaseShared(1);
    }

    public int getCount() {
        return mySync.getCount();
    }
}

自定义Semaphore(共享锁 非公平锁)

/**
 - 自定义Semaphore
 - state:初始值
 - acquire() state-1
 - release() state+1
 - Sync:abstract
 - NonFairSync
 */
public class MySemaphore {
    //AQS
    static abstract class MySync extends AbstractQueuedSynchronizer {

        private static final long serialVersionUID = 8055037635125704937L;

        public MySync(int count) {
            //setState是AQS自带方法,设置state值
            setState(count);
        }

        //尝试释放锁
        @Override
        protected boolean tryReleaseShared(int arg) {
            //返回true表示doReleaseShared --> 释放锁

            //考虑多线程共享,并发操作要保证state共享变量的原子性-->自旋+CAS
            for (; ; ) {
                //原值
                int oldState = getState();
                int newState = oldState + arg;
                //compareAndSetState是AQS中的方法
                if (compareAndSetState(oldState, newState)) {
                    return true;
                }
            }
        }
    }

    //非公平
    static final class NonFairSync extends MySync {
        private static final long serialVersionUID = -2352767803293687627L;

        public NonFairSync(int count) {
            super(count);
        }

        @Override
        protected int tryAcquireShared(int arg) {
            for (; ; ) {
                //非公平锁,直接上来就枪锁
                int oldState = getState();
                int newState = oldState - arg;//请求后,源资源数减少
                //返回值<0,阻塞
                if (newState < 0 || compareAndSetState(oldState, newState)) {
                    return newState;
                }
            }
        }
    }

    private final MySync mySync;

    public MySemaphore(int count) {
        this.mySync = new NonFairSync(count);
    }

    public void acquire() {
        mySync.acquireShared(1);
    }

    public void release() {
        mySync.releaseShared(1);
    }
}

自定义ReentrantLock(独占锁)

/**
 - A线程lock()时,会调用tryAcquire()独占该锁并将state+1
 - 此时,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁
 - 当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念
 - 但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的
 - 
 - 自定义ReentrantLock
 - 要求:非可重入锁,独占锁
 */
public class MyReentrantLock implements Lock {

    //state=0当前线程没有获取到锁
    //state=1当前线程获取到锁
    private static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 6712919921457609668L;

        @Override //判断是否占用
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        //独占锁:一次只能有一个线程获取到锁,此时state=0才能成功返回true
        //独占锁只有两个状态
        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0, 1)) {//原值0,新值1,改成功返回true,则获取锁
                //将当前线程设置为执行线程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        //释放锁
        @Override
        protected boolean tryRelease(int arg) {
            if (getState() == 0) {//没有获取到锁
                throw new UnsupportedOperationException();
            }
            //设置当前执行的线程为null
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        Condition newCondition() {
            return new ConditionObject();
        }
    }

    private final Sync sync = new Sync();

    @Override
    public void lock() {//直接获取锁
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        //响应中断式直接获取锁
        sync.acquireInterruptibly(1);
    }

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

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

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

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}

三元共享同步工具类

  • 与Semaphore类似
  • 每次同时运行三个线程
public class TrinityLock implements Lock {
    //为3表示允许两个线程同时获得锁
    private final Sync sync = new Sync(3);

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5919240659191346108L;

        Sync(int count) {
            if (count <= 0) {
                throw new IllegalArgumentException("count must large than zero.");
            }
            setState(count);
        }

        @Override
        public int tryAcquireShared(int reduceCount) {
            for (; ; ) {
                int current = getState();
                int newCount = current - reduceCount;
                if (newCount < 0 || compareAndSetState(current, newCount)) {
                    return newCount;
                }
            }
        }

        @Override
        public boolean tryReleaseShared(int returnCount) {
            for (; ; ) {
                int current = getState();
                int newCount = current + returnCount;
                if (compareAndSetState(current, newCount)) {
                    return true;
                }
            }
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    }

    @Override
    public void lock() {
        sync.acquireShared(1);
    }

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

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

    @Override
    public boolean tryLock() {
        return sync.tryAcquireShared(1) >= 0;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(time));
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-26 11:28:38  更:2022-04-26 11:28:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:34:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码