Semaphore与ReentrantLock的区别
本文将会从以下四个方面探究Semaphore与ReentrantLock的区别:使用场景、实现方式、性能比较、与synchronized的比较。
一、使用场景
Semaphore是一种信号量(Semaphore)实现,ReentrantLock是一个互斥锁的实现,因此它们的使用场景也不同。
Semaphore主要用于控制资源的访问数量,比如控制线程的并发访问数量,或者控制资源的池化等。
ReentrantLock则是用于控制对共享资源的互斥访问,它比synchronized更加灵活,可以提供公平锁和非公平锁机制,可以中断等待锁的线程,而synchronized则不提供这些功能。
二、实现方式
Semaphore是基于AQS实现的,具体来说Semaphore维护了一个计数器,可以通过调用acquire()方法来获取一个资源的访问权,如果计数器大于零,则计数器减1,如果计数器等于0,则阻塞线程,等待其他线程释放资源。释放资源的线程则通过调用release()方法来将计数器加1,唤醒等待的线程。
ReentrantLock也是基于AQS实现的,具体来说ReentrantLock维护了一个锁状态和一个线程的等待队列,锁状态用于判断锁是否被获得,线程的等待队列用于存放被阻塞的线程。通过调用lock()方法获得锁,如果锁已经被其他线程占用,则将当前线程加入到等待队列中,等待其他线程释放锁。释放锁的线程则通过调用unlock()方法来释放锁,唤醒其他等待的线程。
三、性能比较
由于Semaphore和ReentrantLock都是基于AQS实现的,它们的性能表现非常接近,通常只有极小的差异。
但是,在特定的场景下,Semaphore可能会比ReentrantLock更快,比如需要控制大量线程并发访问,获取锁的开销会占用大量的时间,而不必要的锁竞争也会使得性能下降。因此,Semaphore的优势在于可以通过控制线程访问的并发度来避免锁竞争,进而提高性能。
四、与synchronized的比较
与synchronized相比,Semaphore和ReentrantLock提供了更多的功能和更为灵活的锁机制。具体来说:
- Semaphore和ReentrantLock支持公平锁和非公平锁机制,而synchronized只支持非公平锁。
- Semaphore和ReentrantLock支持中断等待锁的线程,而synchronized不支持。
- Semaphore可以控制线程的并发访问数量,而synchronized和ReentrantLock都只能控制一个线程的获取锁。
- 在特定的场景下,Semaphore的性能可能比synchronized更好,而ReentrantLock的性能则与synchronized相当。
下面是Semaphore和ReentrantLock的代码示例:
// Semaphore的示例 Semaphore sem = new Semaphore(10); // 控制并发访问数量为10的Semaphore for (int i = 0; i < 20; i++) { new Thread(() -> { try { sem.acquire(); // 获取Semaphore许可 // 访问受限资源 sem.release(); // 释放Semaphore许可 } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } // ReentrantLock的示例 ReentrantLock lock = new ReentrantLock(); for (int i = 0; i < 20; i++) { new Thread(() -> { try { lock.lock(); // 获取ReentrantLock锁 // 访问受限资源 } finally { lock.unlock(); // 释放ReentrantLock锁 } }).start(); }