悲观锁、乐观锁、公平锁和非公平锁的原理与使用

  • 发布时间:2023-08-23 23:25:14
  • 本文热度:浏览 196 赞 0 评论 0
  • 全文共1字,阅读约需1分钟

悲观锁、乐观锁、公平锁、非公平锁的原理与使用

概述

在并发编程中,锁是用来保护临界区资源的一种机制。悲观锁、乐观锁、公平锁和非公平锁是常见的锁类型,它们在不同的场景下具有不同的特点和优劣势。本文将详细介绍这四种锁的原理、使用方法和适用场景。

1. 悲观锁

悲观锁认为在整个访问过程中都会发生并发冲突,因此在每次访问共享资源之前都会将其锁定,使其他线程无法访问。常见的悲观锁实现方式是使用互斥锁(Mutex)或者读写锁(ReadWriteLock)。Java中的悲观锁主要通过synchronized关键字和Lock接口的实现类来实现。其中synchronized关键字是隐式锁(互斥锁),而Lock接口的实现类(如ReentrantLock)是显示锁。

悲观锁的示例代码:

使用synchronized来实现悲观锁的示例如下:

public synchronized void synchronizedMethod() {
    // 访问共享资源的代码
}

使用ReentrantLock来实现悲观锁的示例如下:

Lock lock = new ReentrantLock();

public void lockMethod() {
    lock.lock();
    try {
        // 访问共享资源的代码
    } finally {
        lock.unlock();
    }
}

悲观锁的特点:

  • 悲观锁适用于多写少读的场景,可以保证数据的一致性。
  • 悲观锁在多线程竞争下性能较低,因为每次访问共享资源都需要加锁和解锁的操作。

2. 乐观锁

乐观锁认为在整个访问过程中不会发生并发冲突,因此不需要对共享资源加锁,而是在更新数据时通过版本号或者CAS(Compare And Swap)操作来保证数据的一致性。在Java中,Atomic包下的原子类(如AtomicIntegerAtomicReference等)就是使用乐观锁的例子。

乐观锁的示例代码:

import java.util.concurrent.atomic.AtomicInteger;

public class OptimisticLockExample {
    private final AtomicInteger version = new AtomicInteger(0);
    private int resource;

    public void accessResource() {
        int currentVersion = version.get();
        // 读取共享资源

        // 更新共享资源
        if (version.compareAndSet(currentVersion, currentVersion + 1)) {
            // 更新成功
            // 其他操作
        } else {
            // 更新失败
            // 重试或者进行其他处理
        }
    }
}

乐观锁的特点:

  • 乐观锁适用于多读少写的场景,可以提高并发性能。
  • 乐观锁的实现方式相对复杂,需要考虑数据一致性和并发冲突的处理。

3. 公平锁

公平锁是指多个线程按照请求的顺序获取锁资源,即先到先得。使用公平锁可以避免某些线程长时间等待的情况,但也会引入一定的线程切换开销。 Java中的synchronized关键字是一种非公平锁。 Java中的ReentrantLock可以通过构造函数的参数来指定锁的公平性。默认情况下,ReentrantLock是非公平锁。

Lock fairLock = new ReentrantLock(true); // 公平锁
Lock unfairLock = new ReentrantLock(false); // 非公平锁

公平锁的示例代码:

import java.util.concurrent.locks.ReentrantLock;

public class FairLockExample {
    private final ReentrantLock lock = new ReentrantLock(true);

    public void accessResource() {
        try {
            lock.lock(); // 获取锁
            // 访问共享资源
        } finally {
            lock.unlock(); // 释放锁
        }
    }
}

公平锁的特点:

  • 公平锁可以确保线程按照请求顺序获取锁资源,避免饥饿现象。
  • 公平锁的性能较悲观锁和乐观锁稍低,因为需要维护等待队列和进行线程切换。

4. 非公平锁

非公平锁是指多个线程获取锁的顺序是不确定的,可能出现新请求的线程抢占锁资源的情况。使用非公平锁可以提高吞吐量和性能,但可能会导致某些线程长时间等待。

非公平锁的示例代码:

import java.util.concurrent.locks.ReentrantLock;

public class UnfairLockExample {
    private final ReentrantLock lock = new ReentrantLock(false);

    public void accessResource() {
        try {
            lock.lock(); // 获取锁
            // 访问共享资源
        } finally {
            lock.unlock(); // 释放锁
        }
    }
}

非公平锁的特点:

  • 非公平锁允许新请求的线程抢占锁资源,可以提高吞吐量和性能。
  • 非公平锁可能导致某些线程长时间等待,可能会引起线程饥饿现象。

总结

悲观锁、乐观锁、公平锁和非公平锁是常见的并发编程锁类型。它们在不同的场景下有不同的特点和优劣势。悲观锁适用于多写少读的场景,可以保证数据的一致性;乐观锁适用于多读少写的场景,可以提高并发性能;公平锁可以确保线程按照请求顺序获取锁资源,避免饥饿现象;非公平锁可以提高吞吐量和性能,但可能会导致某些线程长时间等待。

在实际应用中,需要根据具体的场景选择合适的锁类型。同时,锁的使用要尽量精确地控制临界区范围,避免死锁和性能问题。

正文到此结束
评论插件初始化中...
Loading...