java 并发(并发工具包)

java 并发(并发工具包)

##13个原子操作类
####原子基本类型

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong

常用方法如下:
int addAndGet(int delta): 以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。
boolean compareAndSet(int expect,int update):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。
int getAndIncrement():以原子方式将当前值加1,注意,这里返回的是自增前的值
int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值
int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值

####原子数组
AtomicIntegerArray:原子更新整型数组里的元素
AtomicLongArray:原子更新长整型数组里的元素
AtomicReferenceArray:原子更新引用类型数组里的元素

常用方法如下:
int addAndGet(int i , int delta):以原子方式将输入值与数组中索引i的元素相加。
boolean compareAndSet(int i , int expect , int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值。

####原子更新引用
AtomicReference 原子更新引用类型
AtomicReferenceFieldUpdater 原子更新引用类型里的字段

####原子更新属性
AtomicIntegerFieldUpdater:
AtomicLongFieldUpdater
AtomicStampedReference:

参考:https://www.cnblogs.com/boothsun/p/8010692.html
##并发工具类

  • CountDownLatch

利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行

常用api
public void await() //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public void countDown() { }; //将count值减1

  • CyclicBarrie(同步屏障)
    常用api
    int await()
    int await(long timeout, TimeUnit unit)

  • CyclicBarrier和CountDownLatch的区别

    1. CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的
  • Semaphore

Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可

常用API
1. acquire()
2. release()
3. intavailablePermits():返回此信号量中当前可用的许可证数
4. intgetQueueLength():返回正在等待获取许可证的线程池
5. booleanhasQueuedThreads():是否有线程正在等待获取许可证
6. Collection getQueuedThreads():返回所有等待获取许可证的线程集合

##阻塞队列

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素

  • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列
  • LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列
  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列
  • SynchronousQueue:一个不存储元素的阻塞队列
  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列
  • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列

阻塞队列提供了四种处理方法:
-w821
在这里插入图片描述

  1. 抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出IllegalStateException(“Queue full”)异常。当队列为空时,从队列里获取元素时会抛出NoSuchElementException异常 。
  2. 返回特殊值:插入方法会返回是否成功,成功则返回true。移除方法,则是从队列里拿出一个元素,如果没有则返回null
  3. 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用
  4. 超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出

###阻塞队列的实现原理

public ArrayBlockingQueue(int capacity, boolean fair) {
        //省略其他代码
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            insert(e);
        } finally {
            lock.unlock();
        }
}

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return extract();
  } finally {
            lock.unlock();
        }
}

private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        ++count;
        notEmpty.signal();
    }

##线程池
###线程池的处理流程

  • 线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程
  • 线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程
  • 线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务

###基础参数

  • corePoolSize(线程池的基本大小)
  • maximumPoolSize(线程池最大数量)
  • runnableTaskQueue(任务队列)
  • new ThreadFactoryBuilder().setNameFormat(“XX-task-%d”).build(); 设置线程池名称前缀
  • keepAliveTime(线程活动保持时间)
  • RejectedExecutionHandler(拒绝策略)

###关闭线程池

  • shutdown 函数 (只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程
  • shutdownNow 函数 首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
  • isTeminated 函数 返回为true 表示线程池关闭成功

##CopyOnWrite组件
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器

##Executor框架
###ThreadPoolExecutor

  • FixedThreadPool 推荐使用
  • SingleThreadExecutor 单个线程
  • CachedThreadPool. 注意线程不够使用的话,会不断创建需要线程,导致资源消耗完

###ScheduledThreadPoolExecutor

class Temp extends Thread {
    public void run() {
        System.out.println("run");
    }
}

public class ScheduledJob {
	
    public static void main(String args[]) throws Exception {
    
    	Temp command = new Temp();
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        
        ScheduledFuture<?> scheduleTask = scheduler.scheduleWithFixedDelay(command, 5, 1, TimeUnit.SECONDS);
    
    }
}

###Runnable接口和Callable接口

Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。它们之间的区别是Runnable不会返回结果,而Callable可以返回结果

##高并发限流策略

###Guava-RateLimiter(令牌捅算法)

令牌桶算法(Token Bucket)和 Leaky Bucket(漏捅) 效果一样但方向相反的算法,更加容易理解.随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务

在这里插入图片描述

令牌桶的另外一个好处是可以方便的改变速度. 一旦需要提高速率,则按需提高放入桶中的令牌的速率. 一般会定时(比如100毫秒)往桶中增加一定数量的令牌, 有些变种算法则实时的计算应该增加的令牌的数量.

缺点:单机限流,在分布式限流中需要结合redis限流重写代码

###源码分析:

####SmoothBursty

RateLimiter

**RateLimiter 中的变量**
/**
   * The currently stored permits.
   */
   现存储的令牌数量
  double storedPermits;

  /**
   * The maximum number of stored permits.
   */
   最大线程数量
  double maxPermits;

  /**
   * The interval between two unit requests, at our stable rate. E.g., a stable rate of 5 permits
   * per second has a stable interval of 200ms.
   */
   发送一个令牌的间隔
  double stableIntervalMicros;

  /**
   * The time when the next request (no matter its size) will be granted. After granting a
   * request, this is pushed further in the future. Large requests push this further than small
   * requests.
   */
   下次可以获取令牌的时间 毫秒
  private long nextFreeTicketMicros = 0L; // could be either in the past or future

SmoothBursty

static final class SmoothBursty extends SmoothRateLimiter {
    /** The work (permits) of how many seconds can be saved up if this RateLimiter is unused? */
    //最多存储多少秒的令牌
    final double maxBurstSeconds; 
    
    SmoothBursty(SleepingStopwatch stopwatch, double maxBurstSeconds) {
      super(stopwatch);
      this.maxBurstSeconds = maxBurstSeconds;
    }
  
    @Override
    void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
      double oldMaxPermits = this.maxPermits;
      //设置最大令牌数
      maxPermits = maxBurstSeconds * permitsPerSecond;
      //设置现在令牌数量
      if (oldMaxPermits == Double.POSITIVE_INFINITY) {
        // if we don't special-case this, we would get storedPermits == NaN, below
        storedPermits = maxPermits;
      } else {
        storedPermits = (oldMaxPermits == 0.0)
            ? 0.0 // initial state
            : storedPermits * maxPermits / oldMaxPermits;
      }
    }
  
    @Override
    long storedPermitsToWaitTime(double storedPermits, double permitsToTake) {
      return 0L;
    }
  }
  
  
获取令牌逻辑

设置睡眠多久获取到所要请求的令牌

public double acquire(int permits) {
    long microsToWait = reserve(permits);
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
  }

  final long reserve(int permits) {
    checkPermits(permits);
    synchronized (mutex()) {
    //返回获取到足够令牌所需要的等待时间
      return reserveAndGetWaitLength(permits, stopwatch.readMicros());
    }
  }
private void resync(long nowMicros) {
    // if nextFreeTicket is in the past, resync to now
    if (nowMicros > nextFreeTicketMicros) {
    //设置到现在可以获取到的令牌数
      storedPermits = min(maxPermits,
          storedPermits + (nowMicros - nextFreeTicketMicros) / stableIntervalMicros);
      nextFreeTicketMicros = nowMicros;
    }
  }
 @Override
  final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
  //计算设置到当前还可以有多少个令牌可以获取
    resync(nowMicros);
    //记录上一次可获取令牌时间
    long returnValue = nextFreeTicketMicros;
    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    //计算得到可能不够的令牌数量
    double freshPermits = requiredPermits - storedPermitsToSpend;
  //storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend) 返回固定是0 ,计算得到需要等待的时间
    long waitMicros = storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
        + (long) (freshPermits * stableIntervalMicros);

    this.nextFreeTicketMicros = nextFreeTicketMicros + waitMicros;
    this.storedPermits -= storedPermitsToSpend;
    //返回的是之前的可获取令牌时间,如果不想预拿令牌,需要将nextFreeTicketMicros返回即可
    return returnValue;
  }

参考:
https://segmentfault.com/a/1190000012947169 基于redis的RateLimiter
http://ifeve.com/guava-ratelimiter/
https://www.cnblogs.com/softidea/p/6229543.html
https://segmentfault.com/a/1190000012875897

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值