返回

锁的用法及常见面试题

Android

Lock的意义和使用

在多线程编程中,多个线程同时操作共享数据时,可能会引发数据不一致的情况。例如,两个线程同时对同一个变量进行写操作,可能会导致数据的丢失。为了解决这个问题,Java提供了锁(Lock)机制。

锁是一种同步机制,它允许一个线程在访问共享数据时,独占该数据的访问权。这样,其他线程只能等待锁被释放,才能访问共享数据。

使用锁可以保证共享数据的安全性,防止数据被破坏。然而,过多的使用锁也会导致性能下降,因为锁会阻塞其他线程的执行。因此,在使用锁时,需要仔细权衡锁的收益和成本。

同步器AbstractQueuedSynchronizer

AbstractQueuedSynchronizer (AQS)是Java中锁的基础类。AQS提供了锁的实现框架,允许开发人员自定义锁的行为。

AQS使用队列来管理等待获取锁的线程。当一个线程试图获取锁时,如果锁已经被其他线程持有,则该线程会被放入队列中等待。当锁被释放后,AQS会唤醒队列中的第一个线程,并允许该线程获取锁。

重入锁ReentrantLock

ReentrantLock是Java中的一种重入锁。所谓重入锁,就是允许一个线程多次获取同一个锁。这意味着,一个线程可以获取一个锁,然后在释放锁之前再次获取该锁。

ReentrantLock使用计数器来记录锁被获取的次数。当一个线程获取锁时,计数器加一;当一个线程释放锁时,计数器减一。当计数器为零时,锁被释放。

读写锁ReentrantReadWriteLock

ReentrantReadWriteLock是Java中的一种读写锁。读写锁允许多个线程同时读共享数据,但只允许一个线程写共享数据。

ReentrantReadWriteLock有两个锁:一个读锁和一个写锁。当一个线程获取读锁时,其他线程只能获取读锁,不能获取写锁。当一个线程获取写锁时,其他线程不能获取读锁或写锁。

Condition

Condition是一个等待队列,它允许线程等待某个条件满足。当条件满足时,线程会被唤醒并继续执行。

Condition与锁一起使用。当一个线程需要等待某个条件满足时,它可以获取锁,然后调用Condition的await()方法进入等待状态。当条件满足时,其他线程可以调用Condition的signal()或signalAll()方法唤醒等待的线程。

一、Lock的使用

Lock的用法非常简单。首先,创建一个Lock对象。然后,在需要获取锁的代码块之前,使用Lock对象的lock()方法获取锁。最后,在需要释放锁的代码块之后,使用Lock对象的unlock()方法释放锁。

例如,以下代码演示了如何使用Lock来保护一个共享变量:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SharedCounter {
    private final Lock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

二、常见面试题

  1. 锁的分类有哪些?

    • 可重入锁(ReentrantLock)
    • 不可重入锁(synchronized)
    • 读写锁(ReentrantReadWriteLock)
    • 独占锁(Exclusive Lock)
    • 共享锁(Shared Lock)
    • 公平锁(Fair Lock)
    • 非公平锁(Unfair Lock)
  2. 什么是死锁?

    • 死锁是指两个或多个线程互相等待,导致都无法继续执行的情况。
  3. 如何避免死锁?

    • 避免环形等待
    • 避免获取锁的顺序依赖
    • 使用超时锁
  4. 什么是饥饿?

    • 饥饿是指一个线程长时间无法获取锁,导致无法执行的情况。
  5. 如何避免饥饿?

    • 使用公平锁
    • 使用优先级队列
    • 使用锁老化机制
  6. 什么是活锁?

    • 活锁是指两个或多个线程互相抢夺锁,导致都无法继续执行的情况。
  7. 如何避免活锁?

    • 使用锁老化机制
    • 使用随机退避算法