《Java并发编程实战》课程笔记(九)

这篇具有很好参考价值的文章主要介绍了《Java并发编程实战》课程笔记(九)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Semaphore:如何快速实现一个限流器?

信号量模型

  • 信号量模型还是很简单的,可以简单概括为:一个计数器,一个等待队列,三个方法。
    《Java并发编程实战》课程笔记(九)
  • 在信号量模型里,计数器和等待队列对外是透明的,所以只能通过信号量模型提供的三个方法来访问它们,这三个方法分别是:init()、down() 和 up()。
    • init():设置计数器的初始值。
    • down():计数器的值减 1;如果此时计数器的值小于 0,则当前线程将被阻塞,否则当前线程可以继续执行。
    • up():计数器的值加 1;如果此时计数器的值小于或者等于 0,则唤醒等待队列中的一个线程,并将其从等待队列中移除。
    • 在 Java SDK 里面,信号量模型是由 java.util.concurrent.Semaphore 实现的,Semaphore 这个类能够保证这三个方法都是原子操作。
    • 在 Java SDK 并发包里,down() 和 up() 对应的则是 acquire() 和 release()。

如何使用信号量

快速实现一个限流器

  • 实现一个互斥锁,仅仅是 Semaphore 的部分功能,Semaphore 还有⼀个功能是 Lock 不容易实现的,那就是:Semaphore 可以允许多个线程访问一个临界区
    • 比较常见的需求就是我们工作中遇到的各种池化资源,例如连接池、对象池、线程池等等。
    • 其中,数据库连接池,在同一时刻,一定是允许多个线程同时使用连接池的,当然,每个连接在被释放前,是不允许其他线程使用的。
  • 所谓对象池,指的是一次性创建出 N 个对象,之后所有的线程重复利用这 N 个对象,当然对象在被释放前,也是不允许其他线程使用的。
    • 对象池,可以用 List 保存实例对象,这个很简单。
    • 但关键是限流器的设计,这里的限流,指的是不允许多于 N 个线程同时进入临界区。
    • 那如何快速实现⼀个这样的限流器呢?如果我们把计数器的值设置成对象池里对象的个数 N,就能完美解决对象池的限流问题了。
      import java.util.List;
      import java.util.Vector;
      import java.util.concurrent.Semaphore;
      import java.util.function.Function;
      
      public class ObjPool<T, R> {
          final List<T> pool;
          // ⽤信号量实现限流器
          final Semaphore semaphore;
          // 构造函数
          ObjPool(int size, T t) {
              pool = new Vector<T>(){};
              for (int i = 0; i < size; i++) {
                  pool.add(t);
              }
              semaphore = new Semaphore(size);
          }
      
          // 利⽤对象池的对象,调⽤ func
          // function 的作用是转换,将一个值转为另外一个值
          R exec(Function<T, R> function) throws InterruptedException {
              T t = null;
              semaphore.acquire();
              try {
                  t = pool.remove(0);
                  return function.apply(t);
              } finally {
                  pool.add(t);
                  semaphore.release();
              }
          }
      
          public static void main(String[] args) throws InterruptedException {
              // 创建对象池
              ObjPool<Long, String> objPool = new ObjPool<Long, String>(10, 2L);
              // 通过对象池获取 t,之后执⾏
              objPool.exec(t -> {
                  System.out.println(t);
                  return t.toString();
              });
          }
      }
      
    • 我们用一个 List来保存对象实例,用 Semaphore 实现限流器。
    • 关键的代码是 ObjPool 里面的 exec() 方法,这个方法里面实现了限流的功能。
      • 在这个方法里面,我们首先调用 acquire() 方法(与之匹配的是在 finally 里面调用 release() 方法),假设对象池的大小是 10,信号量的计数器初始化为 10,那么前 10 个线程调用 acquire() 方法,都能继续执行,而其他线程则会阻塞在 acquire() 方法上。
      • 我们为每个线程分配了一个对象 t(这个分配工作是通过 pool.remove(0) 实现的),分配完之后会执行一个回调函数 func,而函数的参数正是前面分配的对象 t ;执行完回调函数之后,它们就会释放对象(这个释放工作是通过 pool.add(t) 实现的),同时调用 release() 方法来更新信号量的计数器。
      • 如果此时信号量里计数器的值小于等于 0,那么说明有线程在等待,此时会自动唤醒等待的线程。

文章来源地址https://www.toymoban.com/news/detail-472092.html

到了这里,关于《Java并发编程实战》课程笔记(九)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《C++并发编程实战》读书笔记(3):并发操作的同步

    当线程需要等待特定事件发生、或是某个条件成立时,可以使用条件变量 std::condition_variable ,它在标准库头文件 condition_variable 内声明。 wait() 会先在内部调用lambda函数判断条件是否成立,若条件成立则 wait() 返回,否则解锁互斥并让当前线程进入等待状态。当其它线程调用

    2024年02月10日
    浏览(26)
  • 《C++并发编程实战》读书笔记(4):原子变量

    标准原子类型的定义位于头文件 atomic 内。原子操作的关键用途是取代需要互斥的同步方式,但假设原子操作本身也在内部使用了互斥,就很可能无法达到期望的性能提升。有三种方法来判断一个原子类型是否属于无锁数据结构: 所有标准原子类型( std::atomic_flag 除外,因为

    2024年02月10日
    浏览(27)
  • 《C++并发编程实战》读书笔记(1):线程管控

    包含头文件 thread 后,通过构建 std::thread 对象启动线程,任何可调用类型都适用于 std::thread 。 启动线程后,需要明确是等待它结束、还是任由它独自运行: 调用成员函数 join() 会先等待线程结束,然后隶属于该线程的任何存储空间都会被清除, std::thread 对象不再关联到已结

    2024年02月10日
    浏览(30)
  • Java并发编程实战

    2023年06月19日
    浏览(39)
  • 《C++并发编程实战》读书笔记(2):线程间共享数据

    在C++中,我们通过构造 std::mutex 的实例来创建互斥量,调用成员函数 lock() 对其加锁,调用 unlock() 解锁。但通常更推荐的做法是使用标准库提供的类模板 std::lock_guard ,它针对互斥量实现了RAII手法:在构造时给互斥量加锁,析构时解锁。两个类都在头文件 mutex 里声明。 假设

    2024年02月10日
    浏览(33)
  • java并发编程之美第五章读书笔记

    CopyOnWriteArrayList 线程安全的ArrayList,对其进行的修改操作都是在底层的一个复制的数组(快照)进行的,也就是写时复制策略 类图 每一个对象里面有一个array数组进行存放具体的元素,ReentrantLock独占锁对象用来保证同时只有一个线程对array进行修改,这里只要记得ReentrantLock是独占锁

    2024年02月03日
    浏览(35)
  • Java并发编程学习笔记(一)线程的入门与创建

    认识 程序由指令和数据组成,简单来说,进程可以视为程序的一个实例 大部分程序可以同时运行多个实例进程,例如记事本、画图、浏览器等 少部分程序只能同时运行一个实例进程,例如QQ音乐、网易云音乐等 一个进程可以分为多个线程,线程为最小调度单位,进程则是作

    2024年02月16日
    浏览(39)
  • 【项目实战】并发编程之Java中使用五种方式实现延时执行调用

    To实现延时执行调用,Java中可以使用Thread.sleep()方法。该方法接受一个以毫秒为单位的时间参数,使当前线程休眠指定的时间。在休眠期间,线程不会执行任何操作。 以下是一个示例代码块,演示如何使用Thread.sleep()方法实现延时执行调用:

    2024年02月15日
    浏览(26)
  • 多线程控制并发数目工具类Semaphore

    在多线程编码过程中,我们会用到多线程来提升运行效率。比如我们的Executors创建线程池,程序尽可能的压榨CPU资源来提升我们程序吞吐量。但是过度的使用线程,也会将我们CPU资源榨干,从而让我们系统不能正常的提供服务。故今天我们引入JUC并发包下面的semaphore并发类,

    2024年02月02日
    浏览(32)
  • 【JAVA开发面试】如何处理并发访问如何进行代码的单元测试Java多线程编程消息中间件设计模式技术难题是如何解决的

    【 点我-这里送书 】 本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题 中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明

    2024年02月03日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包