zoukankan      html  css  js  c++  java
  • AQS的使用和reentrantlock

    AQS的使用和reentrantlock

    AQS概述

    AQS是一个同步器,全称是AbstractQueuedSynchronizer类。

    使用方法:子类继承AQS,然后重写tryAcquire、tryRelease、isHeldExclusively(如果是共享模式实现tryAcquireShared和tryReleaseShared方法),然后将其作为内部类,外部类定义unlock和lock方法并调用acquire和release方法即可(共享模式调用acquireShared和releaseShared)。

    线程协调场景主要分为两类,一个线程在进行操作时不允许其他线程操作,即独占模式,还有允许多个线程操作的情况,称为共享模式。(独占和共享两种模式可以两个同时实现)

    一个关键变量:内部封装了一个volatile修饰的int类型的字段state,代表当前同步状态。

    定义了访问该字段的几个方法getState()、setState(val)、compareAndSetState(expect, update)

    独占模式

    独占模式下保证线程同步的操作是这样的,将state设为0,当某个线程要独占时都需要先判断state是不是0,如果不是就进入阻塞状态等待,如果是0就把state设置为1。然后进行操作,实现这个操作需要设置3种操作:分别是尝试获取同步状态、释放同步状态、判断是否有线程独占。

    这三种操作分别对应下面三个方法tryAcquire(tryAcquireNanos方法加了超时限制,超时就自动视为获取失败)、tryRelease、isHeldExclusively。

    public class PlainLock {
    
    	private static class Sync extends AbstractQueuedSynchronizer{
    
    		@Override
    		protected boolean tryAcquire(int arg) {
    			// TODO Auto-generated method stub
    			return compareAndSetState(0, 1);
    		}
    
    		@Override
    		protected boolean tryRelease(int arg) {
    			// TODO Auto-generated method stub
    			setState(0);
    			return true;
    		}
    
    		@Override
    		protected boolean isHeldExclusively() {
    			// TODO Auto-generated method stub
    			return getState() == 1;
    		}
    	}
    	
    	private Sync sync = new Sync();
    	public void lock() {
    		sync.acquire(1);
    	}
    	
    	public void unlock() {
    		sync.release(1);
    	}
    }
    

    共享模式

    当同步器使用共享模式时,区别在于允许多个线程持有资源,这个数量就是state的初始值,共享模式下需要定义两个方法tryAcquireShared(val)、tryReleaseShared(val),也是尝试获得和释放同步状态,尝试获得时返回值为int数,返回一个非负数代表获得同步状态成功(需要在aqs的构造方法中设置setState方法来确定可以同时并发的线程),然后重定义lock和unlock方法内部使用acquireShared和releaseShared方法。

    //保证不超过两个线程同时访问的锁
    public class DoubleLock {
    
    	private class Sync extends AbstractQueuedSynchronizer{
    		
    		public Sync() {
    			super();
    			setState(2);
    		}
    		
    		//共享模式下需要一开始在构造方法里设置state的值
    		//然后在重写tryAcquireShared和tryReleaseShared方法
    		
    		//该返回值大于等于0说明获取同步状态成功,否则加入同步队列
    		@Override
    		protected int tryAcquireShared(int arg) {
    			// TODO Auto-generated method stub
    			while(true) {
    				int now = getState();
    				int next = getState() - arg;
    				if(compareAndSetState(now, next)) {
    					return next;
    				}
    			}
    		}
    
    		@Override
    		protected boolean tryReleaseShared(int arg) {
    			// TODO Auto-generated method stub
    			while(true) {
    				int now = getState();
    				int next = getState() + arg;
    				if(compareAndSetState(now, next)) {
    					return true;
    				}
    			}
    		}
    	}
    	
    	private Sync sy = new Sync();
    	
    	public void lock() {
    		sy.acquireShared(1);
    	}
    	
    	public void unlock() {
    		sy.releaseShared(1);
    	}	
    }
    

    acquire和release方法

    AQS内部维护了一个node类,node中有两个指针和一个线程Thread,AQS内部维护了两个node,分别是node队列的头和尾节点。

    acquire(args)方法就是获取同步状态,如果成功就返回,如果失败就将本线程加入同步队列。内部首先调用tryAcquire(args)方法尝试获取同步状态,如果失败之后会调用addWaiter和acquireQueued方法封装node插入同步队列并再次尝试获取,失败后会检查队列中node的状态,修改node的状态并阻塞。

    (还可以使用acquireInterruptibly方法,它是可中断的,如果没获取到而阻塞,其他线程中断了该线程就会抛出interruptedEx异常)

    release(args)方法就是释放同步状态,会调用tryrelease方法,如果成功了就唤醒同步队列的下一个节点,并释放头节点。

    reentrantlock的实现

    Reentrantlock内部定义了一个AQS的子类,每次new一个Reentrantlock就相当于new了一个AQS,内部封装着一个同步队列,AQS内部还有一个ConditionObject的内部类,这个内部类内部维护了两个node类型变量,相当于维护了一个队列,AQS中的队列和这个ConditionObject中的队列用的是同一个node。

    Reentrantlock有一个方法newCondition,这个方法会返回一个ConditionObject类,也就是说每调用newCondition方法一次,就会构建出一个队列(这个等待队列是单向链表,而aqs同步队列是双向链表)。

    ConditionObject类实现了condition接口,这个接口内部有await和signal方法,await方法执行后,该线程对应的node就会被放入对应condition的队列中,从AQS同步队列中取下(当取下node后AQS同步队列为空时那么最后这一个node就不会被取下),同时改变node的等待状态,signal方法负责唤醒线程,就是将对应condition中的node取下再放回AQS同步队列中去,同时改变node的等待状态。

    Reentrantlock这种可以创建多个condition,也就是创建了多个等待队列,可以实现特定的唤醒,使一个显式锁对应多个等待队列的效果。

  • 相关阅读:
    Mac 终端命令使用自动补全时忽略大小写设置
    Android App专项测试
    评估产品机会
    如何快速获取ListView的打气筒对象
    js处理日期格式yyyy-MM-dd hh:mm:ss
    websocket聊天时,图片压缩处理(url或者input-file)
    canvas图片压缩,局部放大,像素处理
    vscode 右键文件或者文件夹显示菜单
    HTML5-SQLLite连接
    ie下div模拟的表格,表头表体无法对齐
  • 原文地址:https://www.cnblogs.com/yinyunmoyi/p/11532096.html
Copyright © 2011-2022 走看看