程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

阿里一面: lock 和 synchronized 的区别

balukai 2025-07-10 13:08:21 文章精选 3 ℃

Java 小伙伴们!今天咱要来一场惊心动魄的锁之大战,主角就是 Java 世界里的两位强大 “锁将”——lock 和 synchronized。这可不是一场普通的较量,它关乎着你的代码效率、稳定性和可维护性。准备好了吗?让我们一起揭开这场大战的神秘面纱!

想象一下,你正在构建一个复杂的 Java 应用程序,就如同在打造一座宏伟的城堡。而锁,就是这座城堡的守护者,确保一切都有条不紊地运行。那么,lock 和 synchronized 到底有何不同呢?

首先,灵活性上有大不同。synchronized 是 Java 内置的关键字,使用起来相对简单直接。它的主要作用就是确保在同一时刻,只有一个线程能够访问被synchronized修饰的代码块或方法。

当synchronized修饰代码块时,锁的是括号里指定的对象。比如:

public class SynchronizedBlockExample {
    private Object lockObject = new Object();

    public void performTask() {
        synchronized (lockObject) {
            // 这里只有一个线程能进入,锁的是 lockObject 对象
            // 执行任务
        }
    }
}

当synchronized修饰方法时,锁的是调用这个方法的对象实例。如果是静态方法,则锁的是对应的类对象。比如:

public class SynchronizedMethodExample {
    public synchronized void instanceMethod() {
        // 这里只有一个线程能进入这个对象的此方法,锁的是当前对象实例
        // 执行任务
    }

    public static synchronized void staticMethod() {
        // 这里只有一个线程能进入这个类的此静态方法,锁的是类对象
        // 执行任务
    }
}

synchronized 的适用场景主要是在一些简单的同步需求中。例如,一个简单的计数器类,只需要确保对计数器的增加和减少操作是线程安全的,这时使用synchronized就很方便。

public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized void decrement() {
        count--;
    }

    public int getCount() {
        return count;
    }
}

但 lock 呢,它来自于 Java 的并发包,提供了更多的灵活性。比如,lock 可以尝试非阻塞地获取锁,如果获取失败可以立即返回而不会阻塞线程。想象一下,你正在进行一场紧张的任务,如果使用 lock 的tryLock()方法,就可以快速判断是否能够获取锁,不行就赶紧去做别的事情,而不是傻傻地在那里等待。

import java.util.concurrent.locks.ReentrantLock;

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

    public void performTask() {
        if (lock.tryLock()) {
            try {
                // 执行任务
            } finally {
                lock.unlock();
            }
        } else {
            // 无法获取锁时的处理
        }
    }
}

其次,可中断性也是一个关键区别。在某些情况下,我们可能需要中断一个正在等待锁的线程。这时候,lock 的lockInterruptibly()方法就大显神威了。它的作用是让等待锁的线程在被中断时能够抛出InterruptedException,从而可以让我们优雅地处理中断。而 synchronized 可没有这个功能哦。

比如在一个需要响应外部中断信号的任务中,使用 lock 就很合适。

import java.util.concurrent.locks.ReentrantLock;

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

    public void performTask() {
        try {
            lock.lockInterruptibly();
            try {
                while (true) {
                    // 执行长时间任务
                    if (Thread.currentThread().isInterrupted()) {
                        break;
                    }
                }
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            // 处理中断
        }
    }
}

再者,lock 可以与Condition对象结合使用,实现更加精细的线程间通信和等待 / 通知机制。我们可以创建多个Condition对象,每个对象可以代表不同的等待条件,从而实现更复杂的同步逻辑。而 synchronized 就没有这么灵活啦。

例如在一个生产者消费者模型中,使用 lock 和 Condition 可以更好地实现线程间的协调。

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

class ProducerConsumerExample {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    private int[] buffer = new int[5];
    private int count = 0;
    private int in = 0;
    private int out = 0;

    public void put(int value) throws InterruptedException {
        lock.lock();
        try {
            while (count == buffer.length) {
                notFull.await();
            }
            buffer[in] = value;
            in = (in + 1) % buffer.length;
            count++;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public int take() throws InterruptedException {
        int value;
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await();
            }
            value = buffer[out];
            out = (out + 1) % buffer.length;
            count--;
            notFull.signal();
            return value;
        } finally {
            lock.unlock();
        }
    }
}

lock 的适用场景则更加广泛,尤其是在需要复杂的同步逻辑和对线程的精细控制时。比如在多线程的生产消费者模式中,使用 lock 和 Condition 可以更好地实现线程间的协调。在需要中断等待锁的线程的情况下,lock 也是首选。

总的来说,lock 和 synchronized 都有各自的优势和适用场景。如果你追求简单直接,synchronized 可能是个不错的选择,它能有效地防止多个线程同时访问共享资源,确保数据的一致性。但如果你需要更多的灵活性和控制能力,那么 lock 绝对是你的得力助手。在实际编程中,要根据具体的需求来选择合适的锁机制,这样才能打造出高效、稳定的 Java 应用程序。

快来加入这场锁之大战的讨论吧!分享你的经验和见解,让我们一起在 Java 的世界里探索更多的奥秘。如果大家有任何问题或者想法,欢迎在评论区留言讨论哦!让我们一起在 Java 的世界里创造更多的精彩。

最近发表
标签列表