synchronized的常见面试题


synchronized 关键字是Java中实现线程同步的基本机制之一。它用于保护共享资源不被多个线程同时访问,从而避免数据不一致和保证
线程安全。synchronized 可以用于方法和代码块,有助于确保在同一时刻只有一个线程可以进入同步代码块。

1.synchronized 是如何实现线程同步的?

  • 同步代码块:通过monitorenter和monitorexit字节码指令实现
  • 同步方法:通过方法访问标志ACC_SYNCHRONIZED实现,JVM检查方法的ACC_SYNCHRONIZED标志,若存在,则自动获取对象Monitor锁,方法执行完毕后释放锁.

Monitor 被翻译为监视器或管程

每个 Java 对象都可以关联一个 Monitor 对象,Monitor 也是 class,其实例存储在堆中,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针,这就是重量级锁

无论是同步代码块还是同步方法,最终都是通过Monitor 对象来管理锁的获取和释放,Monitor 对象锁机制依赖于操作系统的互斥锁。

Java的对象有一个头 (0bject Header),其中包含一个 mark word ,存储锁状态(无锁/偏向锁/轻量级锁/重量级锁)及指向Monitor的指针

JVM会根据竞争情况自动进行状态变换以优化性能。

2.使用方法

原则上:

  • 锁对象建议使用共享资源
  • 在实例方法中使用 this 作为锁对象,锁住的 this 正好是共享资源
  • 在静态方法中使用类名 .class 字节码作为锁对象,因为静态成员属于类,被所有实例对象共享,所以需要锁住类

同步代码块格式:

synchronized(锁对象){
	// 访问共享资源的核心代码
}

实例:

public class demo {
    static int counter = 0;
    //static修饰,则元素是属于类本身的,不属于对象  ,与类一起加载一次,只有一个
    static final Object room = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                synchronized (room) {
                    counter++;
                }
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                synchronized (room) {
                    counter--;
                }
            }
        }, "t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(counter);  //最终打印结果为0
    }
}

3.Synchronized与ReentrantLock的区别

Synchronized 和 ReentrantLock 都是java中用于实现线程同步的机制,它们各有优缺点和适用场景。以下是它们的比较和适用示例:

ReentrantLock 相对于 synchronized 具备如下特点:

  1. 锁的实现:synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的
  2. 性能:新版本 Java 对 synchronized 进行了很多优化,synchronized 与 ReentrantLock 大致相同
  3. 使用:ReentrantLock 需要手动解锁,synchronized 执行完代码块自动解锁
  4. 可中断:ReentrantLock 可中断,而 synchronized 不行
  5. 公平锁:公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁
    • ReentrantLock 可以设置公平锁,synchronized 中的锁是非公平的
    • 不公平锁的含义是阻塞队列内公平,队列外非公平
  6. 锁超时:尝试获取锁,超时获取不到直接放弃,不进入阻塞队列
    • ReentrantLock 可以设置超时时间,synchronized 会一直等待
  7. 锁绑定多个条件:一个 ReentrantLock 可以同时绑定多个 Condition 对象,更细粒度的唤醒线程
  8. 两者都是可重入锁

适用场景:

  • synchronized:适用于简单的同步需求。由于其语法简单且嵌入在Java语言中,特别适合锁定范围与方法等价的情况。小规模、多线程竞争不高的情况下表现优异。适合开发者不想处理锁的复杂生命周期时使用。
  • ReentrantLock:适用于需要更高级的同步控制,或者锁定范围与方法不同时。特别是在需要公平锁、可中断锁操作、尝试获取带超时功能的锁,或者需要多个条件等待时,应选择 ReentrantLock。当系统规模较大、线程数较多,且具有复杂同步需求的情境时表现突出。
  • 选择一个合适的锁机制,将有助于提升应用程序的性能并简化并发代码的编写。

文章作者: Fuchanglai
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Fuchanglai !
赏
  目录