Java面试题:谈谈synchronized和Lock的区别?

简单谈谈 Java 中 synchronized 和 Lock的区别?

区别一:使用方式

synchronized 是关键字,就和 if...else... 一样,是语法层面的实现。因此 synchronized 获取锁以及释放锁都是 Java 虚拟机(JVM)帮助用户完成的。

ReentrantLock 是类层面的实现,因此锁的获取以及锁的释放都需要用户自己去操作。

特别再次提醒,ReentrantLock 在 lock() 获取锁后,一定要手动 unlock() 释放锁,释放锁一般放在 finally 语句块中。

示例代码:

/** 创建可重入锁 */
private static final ReentrantLock lock = new ReentrantLock();

public void demoSync() {
    synchronized (lock) {
        // 业务代码
    }
}

public void demoLock() {
    lock.lock();
    try {
        // 业务代码
    } finally {
        lock.unlock();
    }
}

区别二:灵活性

synchronized 使用简单,简单意味着不灵活。而 ReentrantLock 的锁机制给用户的使用提供了极大的灵活性。

ReentrantLock 的灵活性在 Hashtable 和 ConcurrentHashMap 中体现得淋漓尽致。synchronized 一锁就锁整个 Hash 表,而 ConcurrentHashMap 则利用 ReentrantLock 实现了锁分离,锁的只是 segment(代码片段) 而不是整个 Hash 表。

部分 ConcurrentHashMap 的源码,仅作参考:

/**
 * Stripped-down version of helper class used in previous version,
 * declared for the sake of serialization compatibility
 */
static class Segment<K,V> extends ReentrantLock implements Serializable {
    private static final long serialVersionUID = 2249069246763182397L;
    final float loadFactor;
    Segment(float lf) { this.loadFactor = lf; }
}

/**
 * Saves the state of the {@code ConcurrentHashMap} instance to a
 * stream (i.e., serializes it).
 * @param s the stream
 * @throws java.io.IOException if an I/O error occurs
 * @serialData
 * the key (Object) and value (Object)
 * for each key-value mapping, followed by a null pair.
 * The key-value mappings are emitted in no particular order.
 */
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // For serialization compatibility
    // Emulate segment calculation from previous version of this class
    int sshift = 0;
    int ssize = 1;
    while (ssize < DEFAULT_CONCURRENCY_LEVEL) {
        ++sshift;
        ssize <<= 1;
    }
    int segmentShift = 32 - sshift;
    int segmentMask = ssize - 1;
    @SuppressWarnings("unchecked")
    Segment<K,V>[] segments = (Segment<K,V>[])
        new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
    for (int i = 0; i < segments.length; ++i)
        segments[i] = new Segment<K,V>(LOAD_FACTOR);
    s.putFields().put("segments", segments);
    s.putFields().put("segmentShift", segmentShift);
    s.putFields().put("segmentMask", segmentMask);
    s.writeFields();

    Node<K,V>[] t;
    if ((t = table) != null) {
        Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
        for (Node<K,V> p; (p = it.advance()) != null; ) {
            s.writeObject(p.key);
            s.writeObject(p.val);
        }
    }
    s.writeObject(null);
    s.writeObject(null);
    segments = null; // throw away
}

区别三:锁策略

synchronized 是不公平锁,而 ReentrantLock 可以指定锁是公平的还是非公平的,可通过 ReentrantLock(boolean fair) 构造方法的 fair 指定公平策略,如果此锁应该使用公平的排序策略,则 fair 参数为 true。

公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁。

非公平锁

非公平锁是多个线程加锁时直接尝试获取锁,能抢到锁到直接占有锁,抢不到才会到等待队列的队尾等待。

区别四:等待/通知机制

synchronized 实现等待/通知机制通知的线程是随机的,ReentrantLock 实现等待/通知机制可以有选择性地通知。

区别五:锁自身信息

和 synchronized 相比,ReentrantLock 提供给用户多种方法用于获取锁的信息,例如:可以获取 Lock 是否被当前线程获得、Lock 被同一个线程调用了几次、Lock 是否被任意线程获取等等

总结

如果只需要锁定简单的方法、简单的代码块,那么考虑使用synchronized,复杂的多线程处理场景下可以考虑使用ReentrantLock。


学习必须与实干相结合。 —— 泰戈尔
0 不喜欢
热门推荐
正版12册 鬼谷子书籍 卡耐基人性的弱点墨菲定律羊皮卷狼道羊

正版12册 鬼谷子书籍 卡耐基人性的弱点墨菲定律羊皮卷狼道羊皮卷书口才三绝为人三会修心三不管人三心理学书籍励志书籍将来的你一定感谢现在拼命的自己赢了自己就赢了世界

励志书籍5册你不努力没有人能给你想要的生活 你若不勇敢谁替...

励志书籍5册你不努力没有人能给你想要的生活 你若不勇敢谁替你坚强 正版青少年励志书籍5本 将来的你一

说说我的看法 -
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号