互斥锁和信号量在Java中是什么?它们之间的主要区别是什么?
互斥锁和信号量在Java中是什么?它们之间的主要区别是什么?
遗憾的是,大家都错过了信号量和互斥锁之间最重要的区别,即“所有权”的概念。
信号量没有所有权的概念,这意味着任何线程都可以释放信号量(这本身可能会导致许多问题,但有助于“死亡检测”),而互斥锁具有所有权的概念(即你只能释放已获得的互斥锁)。
所以对于安全编程并发系统来说,所有权非常重要。我总是建议使用互斥锁而不是信号量(但这会带来性能影响)。
互斥锁也可以支持优先级继承(这可以帮助解决优先级反转问题)和递归(消除一种死锁类型)。
还应指出,有“二进制”信号量和“计数/通用”信号量之分。Java的信号量是计数信号量,因此允许将其初始化为一个大于1的值(而互斥锁只能拥有概念上的计数1)。其他帖子中已经指出了这种方法的有用性。
因此,总结一下:除非你需要管理多个资源,否则我始终建议使用互斥锁而不是信号量。
信号量可以进行计数,而互斥锁只能计数到1。
假设您有一个线程正在运行来接受客户端连接。这个线程最多可以同时处理10个客户端。然后每个新客户端都会设置信号量,直到达到10. 当信号量有10个标志时,您的线程将不再接受新连接。
互斥锁通常用于保护资源。如果您的10个客户端可以访问系统的多个部分,则可以使用互斥锁来保护系统的部分,以便连接到该子系统的1个客户端时,其他人就不能访问。您也可以为此目的使用信号量。互斥锁是一个"互斥信号量"。
ReentrantLock
也是如此。所有这些都是递归的。我不知道任何非递归互斥量的“真实世界”示例(我只在教科书中看到过它们),因此我没有考虑它们。 - finnwMutex(互斥锁)基本上是一种互斥控制,只有一个线程可以同时获得资源。当一个线程获得资源时,其他线程就不允许获得该资源,直到拥有该资源的线程释放它。所有等待获取资源的线程都将被阻塞。
Semaphore(信号量)用于控制执行的线程数量。有一组固定的资源,每次一个线程占用相同数量的资源时,资源计数会减少。当信号量计数达到0时,不允许其他线程获取资源,这些线程会被阻塞,直到其他占用资源的线程释放它们。
简而言之,主要区别在于“有多少个线程可以同时获取资源?”
在IT技术中,mutex用于串行访问资源,而semaphore则限制对资源的访问次数。你可以将mutex视为access count为1的semaphore。无论你将semaphore count设置为多少,都会有多个线程可以访问资源,直到该资源被阻塞。
互斥锁通常被称为二元信号量。虽然信号量可以使用任何非零计数创建,但互斥锁在概念上是具有上限为1的信号量。
计数信号量。概念上来说,信号量维护了一组许可证。每个
acquire()
方法都会阻塞,直到有一个许可证可以获得,然后它就会获取该许可证。每个release()
方法都会释放一个许可证,可能会释放某个正在等待的获取者。但是,实际上没有使用任何许可证对象;Semaphore 只是维护了一个可用数量的计数,并相应地采取行动。
信号量经常用于限制可以访问某些(物理或逻辑)资源的线程数
Java 没有内置的 Mutex API。但是可以将其实现为二进制信号量。
初始化为 1 的信号量,且仅在最多有一个许可证可用时使用,可以用作互斥锁。这更常被称为二进制信号量,因为它只有两种状态:一个许可证可用或零个许可证可用。
当以这种方式使用时,二进制信号量具有以下属性(与许多 Lock 实现不同):“锁”可以由所有者以外的线程释放(因为信号量没有所有权概念)。这在某些专业上下文中可能很有用,例如死锁恢复。
信号量通过许可证限制了访问资源的线程数。互斥锁只允许一个线程访问资源。
没有线程拥有 Semaphore。线程可以通过调用 acquire()
和 release()
方法更新许可证数量。Mutex 只应由持有锁的线程解锁。
当将 Mutex 与条件变量一起使用时,存在隐含的括号——清楚地表明了被保护的程序部分。对于信号量来说,情况并非总是如此,它可能被称为并发编程的 goto —— 它很强大,但过于容易在不确定的结构化方式下使用。
你在比较不可比较的东西,从技术上讲,Semaphore和mutex之间没有区别,这是没有意义的。 Mutex只是一个重要的名称,就像应用程序逻辑中的任何名称一样,它意味着你将一个Semaphore初始化为“1”,通常用于保护资源或受保护变量以确保互斥。
同步对象Semaphore实现了一个经典的交通信号灯。交通信号灯控制着对由计数器共享的资源的访问。如果计数器大于零,则允许访问;如果为零,则拒绝访问。计数器计算允许访问共享资源的权限。因此,要访问该资源,线程必须从交通信号灯获得许可。一般来说,要使用交通信号灯,想要访问共享资源的线程会尝试获取许可证。如果交通信号灯计数大于零,则线程获取许可证,并且交通信号灯计数减少。否则,线程将被锁定,直到它能够获得许可证。当线程不再需要访问共享资源时,它释放许可证,因此交通信号灯计数增加。如果有另一个线程正在等待许可证,则此时它将获得许可证。Java的Semaphore类实现了这种机制。
Semaphore有两个构造函数:
Semaphore(int num)
Semaphore(int num, boolean come)
num 指定许可的初始计数。然后,num 指定可以同时访问共享资源的线程数。如果 num 为 1,则一次只能访问一个线程的资源。通过将 come 设置为 true,您可以保证等待的线程按照它们请求的顺序被授予权限。
Mutex是二进制信号量。它必须初始化为1,以满足先到先服务的原则。这使我们了解到每个Mutex的另一个特殊属性:执行down操作的人必须执行up操作。因此,我们已经获得了对某些资源的互斥访问。
现在你可以看到,Mutex是一般信号量的特例。