当前位置: 首页 > 科技观察

别再跟面试官说你不懂semaphore信号量了!

时间:2023-03-20 15:17:40 科技观察

习惯了阿里面试官的冷笑:你用过semaphore,怎么不说?本质是信号量模型,模型图如下:计数器和等待队列对外是透明的,只能通过提供的三个方法访问。三大方法具体是什么?init()用于设置计数器的初始值。向下()计数器-1。如果此时计数器<0,则当前线程被阻塞。up()计数器+1。如果此时计数器≤0,则唤醒等待队列中的一个线程,将其从【等待队列】中移除。可能有同学觉得这里的判断条件应该≥0,估计你理解为生产者-消费者模型中的生产者。可以反过来想,>0表示没有阻塞线程,所以只有≤0才需要唤醒一个等待线程。down()和up()要成对顺序使用:先调用down(),获取锁并执行完流程后,再调用up()释放锁。在>0的情况下,除非故意先调用up(),否则这也失去了信号量的意义。请注意,这些方法是原子的,由信号量模型的实现来保证。JDK中的信号量模型是由Semaphore实现的,它保证了这三个方法都是原子操作。talkischeap,showmecode?信号量模型中的down()和up()最早称为P操作和V操作,信号量模型也称为PV原语。有些人会用semWait()和semSignal()来表示,名字不同,但语义是一样的。JUC的acquire()和release()分别对应down()和up()。如何使用信号量?就像信号量一样,在通过之前必须先检查它是否是绿灯。比如累加器,count+=1操作是临界区,只允许一个线程执行,也就是说必须保证互斥。假设线程t1和t2同时访问add(),当同时调用acquire时,由于acquire是一个原子操作,只有一个线程(假设t1)会将信号量中的计数器减0,而t2将计数器递减为-1:对于t1,信号量中计数器的值为0,≥0,所以t1不会被阻塞,而是继续执行。对于t2,信号量中counter的值是-1,<0,所以t2被阻塞所以此时只有t1会进入临界区执行count+=1。t1执行release()时,信号量中计数器的值为-1,加1后的值为0,≤0。根据up(),此时等待队列中的t2会被唤醒。因此t2只有在t1执行完临界区中的代码后才有机会进入临界区执行,保证了互斥性。JDK既然提供了Lock,为什么还要提供Semaphore呢?实现互斥量只是信号量功能的一部分。信号量还可以允许多个线程访问临界区。最常见的是各种池化资源,比如数据库连接池,允许多个线程同时使用连接池。在释放之前,不允许其他线程使用每个连接。对象池要求一次创建N个对象,然后所有线程重用这N个对象。当然,在对象未被释放之前,其他线程是不允许使用这些对象的。所以核心是限流器。这里的限流意思是不允许超过N个线程同时进入临界区。如何快速实现这样的限流器?那就是信号量。只需将计数器的值设置为对象池中对象的个数N即可:注意这里使用的是Vector,进入临界区的N个线程是不安全的。添加/删除不安全。例如ArrayListremove():好的,请回家等待通知!本文转载自微信公众号「JavaEdge」,可通过以下二维码关注。转载本文请联系JavaEdge公众号。