互斥锁(Mutex Lock)
- 特点
独占性:互斥锁是一种最基本的锁类型,它确保在任何时刻只有一个线程能够访问被保护的共享资源。当一个线程获取了互斥锁后,其他线程如果试图获取该锁,就会被阻塞,直到持有锁的线程释放锁。例如,在一个多线程程序中对一个全局变量进行修改操作时,使用互斥锁可以保证同一时间只有一个线程能够修改这个变量。
简单高效:实现相对简单,在一些简单的并发场景下,能够有效地防止数据竞争。其操作开销相对较小,主要的时间消耗在于线程的阻塞和唤醒过程。
- 应用场景:适用于大多数简单的共享资源保护场景,如对临界区代码的保护,像在计数器的更新、链表的插入和删除操作等情况下,只要涉及到共享资源的写操作,一般都可以使用互斥锁来保证数据的正确性。
读写锁(Read - Write Lock)
- 特点
区分读写操作:读写锁允许同时有多个线程对共享资源进行读操作,但在进行写操作时是互斥的。读操作之间不会相互阻塞,因为读操作不会改变共享资源的状态。写操作则会独占锁,防止其他线程(无论是读还是写)访问共享资源。
提高并发性能:在多读少写的场景下,可以显著提高系统的并发性能。例如,在一个缓存系统中,多个线程频繁读取缓存数据时可以同时进行,而在更新缓存时则独占锁,避免了频繁的线程阻塞。
- 应用场景:常用于数据库系统、文件系统、缓存系统等场景,这些场景中读操作通常比写操作频繁得多,使用读写锁可以在保证数据一致性的同时,提高系统的整体运行效率。
悲观锁和乐观锁(Pessimistic Lock and Optimistic Lock)
- 特点
(1)悲观锁:假定在对共享资源进行操作时,一定会有其他线程来修改这个资源,所以在操作之前就先获取锁,如数据库中的行锁(在对某一行数据进行操作时,先锁住该行)。这种方式可以保证数据的一致性,但可能会导致并发性能下降,因为即使没有其他线程竞争资源,也会先获取锁。
【悲观锁就像是一个非常谨慎的人。它总是假设最坏的情况,认为在自己使用某个资源(如数据库中的一条数据记录)的时候,一定会有别人来争抢这个资源。所以,它在一开始接触这个资源的时候,就会把资源 “锁住”,就像给资源上了一把锁,别人想要访问这个资源,就必须等它用完并把锁打开。】
(2)乐观锁:假定在对共享资源进行操作时,其他线程不会同时修改这个资源。在操作过程中不会加锁,而是在更新数据时检查数据是否被其他线程修改过。如果没有被修改,则更新成功;如果被修改了,则根据具体情况进行重试或其他处理。例如,在数据库中可以通过版本号或时间戳来实现乐观锁,每次更新数据时检查版本号是否与读取时一致。乐观锁可以提高并发性能,但可能会导致更新失败需要重试的情况。
【乐观锁则像是一个比较乐观的人。它觉得在自己使用资源的时候,别人一般不会来争抢这个资源。所以它在操作资源的时候不会一开始就加锁。但是,在它要更新资源的时候,会先检查一下这个资源有没有被别人修改过。如果没有被修改过,它就顺利地进行更新;如果发现被修改过了,那就需要根据具体情况来决定怎么办,可能是重新读取最新的数据再尝试更新,或者是直接放弃更新操作。】
- 应用场景:悲观锁适用于对数据一致性要求极高,且并发冲突可能性较大的场景,如金融系统中的转账操作等。乐观锁适用于并发冲突概率较低,对性能要求较高的场景,如一些网站的用户信息更新(在并发访问概率较低的情况下)。