• 第二部分:并发工具类16->Semaphore:如何快速实现一个限流器


    1.Semaphore

    信号量,类比红绿灯

    编程中,线程能不能执行,看信号量是否允许

    2.信号量模型

    计数器+等待队列+三个方法

    计数器和等待队列对外是透明的,只能通过提供的三个方法(init,down,up)来访问它们

    init()设置计数器的初始值
    down()计数器值减1,计数器当前值小于0,等钱线程被阻塞,否则当前线程可以继续执行
    up()计数器值加1,计数器的值小于或者等于0,则唤醒等待队列中的一个线程,并将其从等待队列中移除

    init,down,up三个方法都是原子性的,通过信号量模型实现方保证
    java.util.concurrent.Semaphore实现

    信号量模型

    
    class Semaphore{
      // 计数器
      int count;
      // 等待队列
      Queue queue;
      // 初始化操作
      Semaphore(int c){
        this.count=c;
      }
      // 
      void down(){
        this.count--;
        if(this.count<0){
          //将当前线程插入等待队列
          //阻塞当前线程
        }
      }
      void up(){
        this.count++;
        if(this.count<=0) {
          //移除等待队列中的某个线程T
          //唤醒线程T
        }
      }
    }
    

    sdk并发包里,down和up对应的是acquire和release

    3.如何用信号量

    count += 1,临界区,只允许一个线程执行,

    类似互斥锁,进入临界区之前执行down操作,退出临界时执行up操作

    
    static int count;
    //初始化信号量
    static final Semaphore s 
        = new Semaphore(1);
    //用信号量保证互斥    
    static void addOne() {
      s.acquire();
      try {
        count+=1;
      } finally {
        s.release();
      }
    }
    

    多线程T1,T2,同时调用acquire时,acquire是原子操作,只有一个线程能将信号量的计数器变为0,线程T2将计数器减为-1
    对于线程T1,信号量里面计数器值是0,大于等于0,线程T1会继续执行,执行count += 1。
    对于线程T2,信号量里面计数器值是-1,小于0,按照信号量模型描述,T2线程被阻塞。
    T1执行完count+=1操作有,执行release()操作,信号量计数器值加1,变为0,小于等于0,对应信号量模型up描述,T2线程被唤醒,T2在T1执行完代码后获取了执行临界区的机会,保证了互斥性

    4.快速实现限流器

    sdk里面有互斥锁了,干嘛还用semaphore呢
    sempahore可以允许多个线程访问一个临界区

    限流:不允许多与N个线程同时进入临界区

    信号量计数器,设置为1,表示只允许一个线程进入临界区,设置为n,表示同时允许n个线程进入临界区

    对象池的伪代码

    
    class ObjPool<T, R> {
      final List<T> pool;
      // 用信号量实现限流器
      final Semaphore sem;
      // 构造函数
      ObjPool(int size, T t){
        pool = new Vector<T>(){};
        for(int i=0; i<size; i++){
          pool.add(t);
        }
        sem = new Semaphore(size);
      }
      // 利用对象池的对象,调用func
      R exec(Function<T,R> func) {
        T t = null;
        sem.acquire();
        try {
          t = pool.remove(0);
          return func.apply(t);
        } finally {
          pool.add(t);
          sem.release();
        }
      }
    }
    // 创建对象池
    ObjPool<Long, String> pool = 
      new ObjPool<Long, String>(10, 2);
    // 通过对象池获取t,之后执行  
    pool.exec(t -> {
        System.out.println(t);
        return t.toString();
    });
    
    原创:做时间的朋友
  • 相关阅读:
    常用CSS代码大全(工作必备)
    微信开发新增拖动组件--movableview介绍
    CSS---解决文本溢出,换行
    SublimeText 自带格式化代码功能
    后台界面也可以很酷!31个高大上的后台管理系统模版
    漏洞:阿里云盾phpMyAdmin <=4.8.1 后台checkPageValidity函数缺陷可导致GETSHELL
    Linux系统定时备份网站文件到七牛云存储脚本
    php重定向的三种方法分享
    CentOS Gnome字体不清晰
    centos 6.5 安装mplayer
  • 原文地址:https://www.cnblogs.com/PythonOrg/p/14976641.html
Copyright © 2020-2023  润新知