MST

星途 面试题库

面试题:Java中volatile与synchronized在多线程应用中的对比

对比volatile关键字和synchronized关键字在多线程编程中的使用场景、原理及性能差异,详细阐述为什么在某些场景下选择volatile而不是synchronized,反之亦然。
22.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

使用场景

  • volatile:适用于当一个变量需要在线程间可见,且不涉及复杂的同步操作,如状态标记变量。例如,在一个多线程的程序中,一个线程负责检查某个任务是否结束,另一个线程在任务完成时修改这个结束标志变量,使用volatile能保证检查线程及时看到标志的变化。
  • synchronized:用于保护一段代码块或方法,确保同一时间只有一个线程能进入该代码块或执行该方法,适用于对共享资源进行读写操作,且需要保证数据一致性的场景。例如银行转账操作,对账户余额的修改需要保证原子性,就可以使用synchronized

原理

  • volatile:通过内存屏障来实现可见性。当一个变量被声明为volatile时,写操作会在写变量后插入一个写屏障,强制将修改后的值刷新到主内存;读操作会在读变量前插入一个读屏障,强制从主内存读取最新值。但volatile不保证原子性,对于像i++这种复合操作,多个线程同时执行可能会出现数据不一致。
  • synchronized:基于对象监视器(Monitor)实现。当线程进入synchronized修饰的代码块或方法时,会获取对象的监视器锁,其他线程就无法获取该锁,从而实现同步。锁的获取和释放过程会导致线程上下文切换。

性能差异

  • volatile:由于不涉及锁的获取和释放,没有线程上下文切换的开销,性能相对较高,适合读多写少的场景。
  • synchronized:获取和释放锁会带来一定的性能开销,尤其是在高并发场景下,大量线程竞争锁会导致频繁的上下文切换,性能会显著下降。

选择原因

  • 选择volatile而不是synchronized
    • 当只需要保证变量的可见性,不需要保证操作的原子性时,volatile开销更小。例如一个用于控制线程是否停止的标志位,只需要线程能及时看到其变化,不需要保证对其修改的原子性。
    • 在读多写少的场景中,volatile能提供更好的性能,因为它避免了锁竞争带来的开销。
  • 选择synchronized而不是volatile
    • 当对共享资源的操作需要保证原子性时,必须使用synchronized。如对共享变量的复杂计算操作,volatile无法满足原子性要求。
    • 在写操作较多且需要保证数据一致性的场景下,synchronized能通过锁机制确保操作的顺序性和原子性,虽然性能开销大,但能保证数据的正确性。