共享锁有时称为“读锁”,而独占锁有时称为“写锁”。您能解释一下“共享”和“独占”这些术语背后的原因吗?
我写下了这个答案,因为我认为这是一个有趣(而且合适)的比喻:
将一件可以锁住的物品看作是教室里的一个黑板(可锁定),上面有一个老师(写者)和许多学生(读者)。
当老师在黑板上写东西时(独占锁):
因为她正在写作并挡住了你的视线,所以没有人能够阅读 => 如果对象被独占锁定,则无法获得共享锁。
其他老师也不会走过来开始写作,否则黑板就变得不可读并困惑了学生 => 如果对象被独占锁定,则无法获得其他独占锁。
当学生们(共享锁)阅读黑板上的内容时:
他们都可以一起阅读上面的内容 => 多个共享锁可以共存。
老师会等待他们阅读完毕才清除黑板以便继续写作 => 如果一个或多个共享锁已存在,则无法获得独占锁。
这很简单。读锁也被称为共享锁,因为多个进程可以同时读取。读锁的目的是防止另一个进程获取写锁。相比之下,写锁在写操作完成期间会禁止所有其他操作,这就是为什么它被描述为独占。
因此,读锁表示“您现在可以读取,但如果您想要写入,则必须等待”,而写锁表示“您必须等待”。
我知道您正在为学习提供支持,但我忍不住想讲一番。
不当使用锁是性能问题的主要原因。使用区分读写锁的锁定系统是一个好的起点,但仔细设计有时可以消除大部分锁定需求。例如,会话状态永远不应该保存在每个状态元素的一个全局集合中。
我实际上看到过这样的做法。这是一个可怕的设计,导致装箱和每次会话状态更改都需要更改集合,涉及长时间的写锁。开销非常大,实际上将服务器降为单线程行为。
将所有会话状态聚合到一个结构中只是一个巨大的改进。会话状态的更改仅更改会话状态结构的成员的值。由于没有其他会话有机会直接引用会话状态,因此更新的唯一集合是会话列表。因此,在会话期间完全不需要锁定,只需要在开始和结束时进行锁定,吞吐量提高了3000倍。
另一个常见的锁定场景是用户应用程序的线程之间共享的资源。大多数现代框架使用消息而不是锁来解决这个问题。当您“转换到UI线程”时,实际上是将包含函数指针和一些参数(或委托和堆栈帧,具体取决于实现)的消息排队。
排他或写锁定使进程独占地访问指定文件的写入部分。当写锁定生效时,没有其他进程可以锁定该文件的那一部分。
共享或读锁定禁止任何其他进程请求指定文件部分的写锁定。但是,其他进程可以请求读锁定。
更多信息请参见:http://www.gnu.org/software/libc/manual/html_node/File-Locks.html
在数据库方面,原则也是相同的。根据Oracle文档,独占锁模式可防止关联资源被共享。获得该锁模式以修改数据。第一个锁定资源的事务是唯一能够在释放独占锁之前更改资源的事务。
共享锁模式允许关联资源在根据所涉及的操作而共享。多个用户读取数据时可以共享数据,保持共享锁以防止写入者(需要独占锁)进行并发访问。多个事务可以在同一资源上获取共享锁。
独占锁 不允许读取和写入操作。
共享锁 只允许读取操作。