基于AQS的信号量简单实现

学习过AQS之后,对其实现有一些了解了,虽然jdk中已经提供了很多同步工具,但是为了锻炼代码能力,完全处于学习的目的分析这些同步工具的设计方法,并结合AQS实现简单版本的同步工具。下面会通过AQS实现一个信号量同步工具,后续还会有其他同步工具的实现。

首先需要了解AQS的基本用法,既然是第一篇那么先介绍以下如何使用AQS。高性能锁有以下几个需要关注的点:共享变量、互斥访问、查看持有锁的线程、自旋、等待队列、公平性。AQS中围绕这些点进行的封装,提供了一些很方便的接口,我们只需要借助以下这些接口即可完成特殊功能的同步工具。

// 自定义同步器需要实现的接口
// 获得锁的具体实现,只考虑共享变量怎么设置为多少,是否能重入、是否考虑公平性,其他的都交给AQS
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}
// 释放锁的具体实现
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}
// 共享锁
/**
* 可以看到共享模式下返回值是int类型,表示资源的数量,所以该返回值可以表示两层意思:
* 1. 是否获得锁,大于0就表示获得锁
* 2. 当前线程获得锁之后,是否还有剩余资源,用于唤醒其他等待的的线程
* 因此在实现该方法时首先判断资源量是否足够,如果有足够资源才能使用CAS自旋操作获竞争锁
**/
protected int tryAcquireShared(int arg) {
    throw new UnsupportedOperationException();
}
// 共享锁
protected boolean tryReleaseShared(int arg) {
    throw new UnsupportedOperationException();
}

// 实现同步器的辅助方法
// 访问共享变量,表示锁状态
protected final int getState() {
    return state;
}
// 互斥设置共享变量
protected final boolean compareAndSetState(int expect, int update) {
    return STATE.compareAndSet(this, expect, update);
}
// 设置当前线程持有锁,用来实现重入锁
protected final void setExclusiveOwnerThread(Thread thread) {
    exclusiveOwnerThread = thread;
}
// 查看当前是否有线程等待锁,用来实现公平锁
public final boolean hasQueuedPredecessors() {
}

// 同步器对外接口
// 申请锁
public final void acquire(int arg) {
}
// 释放锁
public final boolean release(int arg) {
}
public final void acquireShared(int arg) {
}
public final boolean releaseShared(int arg) {
}

下面来考虑实现信号量

  1. 使用组合模式:Semophore不直接基础与AQS而是通过组合其子类完成同步的功能
  2. Semaphore允许指定数量的线程并发,因此使用AQS的共享锁
  3. Semaphore提供公平锁,结合hasQueuedPredecessors()接口实现公平锁

 

package com.xp.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

public class Semaphore {
  final Sync sync;

  public Semaphore(int permits) {
    this.sync = new NonFairSync(permits);
  }

  public Semaphore(int permits, boolean fair) {
    this.sync = fair ? new FairSync(permits) : new NonFairSync(permits);
  }

  public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
  }

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

  abstract static class Sync extends AbstractQueuedSynchronizer {
    Sync(int permits) {
      setState(permits);
    }
    @Override
    protected boolean tryReleaseShared(int release) {
      for (;;) {
        int cur = getState();
        int remain = cur + release;
        if (compareAndSetState(cur, remain)) {
          return true;
        }
      }
    }
  }

  static final class NonFairSync extends Sync {
    public NonFairSync(int permits) {
      super(permits);
    }

    @Override
    protected int tryAcquireShared(int acquire) {
      for (;;) {
        int cur = getState();
        int remain = cur - acquire;
        if (remain < 0 || compareAndSetState(cur, remain)) {
          return remain;
        }
      }
    }
  }

  static final class FairSync extends Sync {
    public FairSync(int permits) {
      super(permits);
    }

    @Override
    protected int tryAcquireShared(int acquire) {
      for (;;) {
        // 判断是否有其他线程等待锁
        if (hasQueuedPredecessors()) {
          return -1;
        }
        int cur = getState();
        int remain = cur - acquire;
        if (remain < 0 || compareAndSetState(cur, remain)) {
          return remain;
        }
      }
    }
  }

  static class Test {
    public static void main(String[] args) {
      Semaphore semaphore = new Semaphore(2, true);
      ExecutorService executor = Executors.newCachedThreadPool();
      for (int i = 1; i < 11; i++) {
        int finalI = i;
        executor.execute(() -> {
          System.out.println(String.format("线程 %d 申请锁 %d", finalI, System.currentTimeMillis()));
          try {
            semaphore.acquire();
          } catch (InterruptedException e) {
            System.out.println(String.format("线程 %d 申请锁被中断 %d ", finalI, System.currentTimeMillis()));
            return;
          }
          System.out.println(String.format("线程 %d 获得锁 %d", finalI, System.currentTimeMillis()));
          try {
            Thread.currentThread().sleep(3000);
          } catch (InterruptedException e) {
            // no op
          }
          semaphore.release();
          System.out.println(String.format("线程 %d 释放锁 %d", finalI, System.currentTimeMillis()));
        });
      }
      executor.shutdown();
    }
  }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值