Java热门面试题三

Java中的synchronized关键字是如何实现线程同步的?

在Java中,synchronized 关键字是用于控制多个线程对共享资源的访问,以防止在并发环境下出现数据不一致的问题。synchronized 可以用来修饰方法或代码块,以确保在同一时刻只有一个线程可以执行某个方法或代码块。下面详细解释 synchronized 是如何实现线程同步的:

1. 监视器锁(Monitor Lock)

Java中的每个对象都可以作为锁,这种锁被称为监视器锁(Monitor Lock)。当一个线程进入一个对象的 synchronized 方法或代码块时,它会自动获得该对象的锁,从而阻止其他线程同时进入该对象的任何其他 synchronized 方法或代码块。

2. 修饰方法

synchronized 修饰一个方法时,这个锁就是该方法的调用对象(即 this 关键字所引用的对象)。如果方法是静态的,那么锁就是该类的 Class 对象。

public class Counter {
    private int count = 0;

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

    public static synchronized void staticIncrement(Counter counter) {
        counter.count++; // 注意,这里的锁是Counter.class,而不是counter对象
    }
}

3. 修饰代码块

synchronized 修饰一个代码块时,可以指定一个对象作为锁。这提供了更细粒度的锁控制,因为可以只锁定那些需要同步的代码部分。

public class Counter {
    private final Object lock = new Object();
    private int count = 0;

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

4. 锁的行为

  • 获取锁:当线程尝试进入 synchronized 方法或代码块时,如果该锁已被其他线程持有,则线程会被阻塞,直到锁被释放。
  • 释放锁:线程执行完 synchronized 方法或代码块后,会自动释放锁。如果线程在执行过程中发生异常而退出,锁也会被释放。
  • 重入性:一个线程可以多次获得同一个锁,这被称为锁的可重入性。Java中的 synchronized 锁是可重入的,这意呀着同一个线程可以在持有锁的情况下再次请求该锁。

5. 锁的升级与降级

虽然Java的 synchronized 关键字本身不支持锁的升级(如从偏向锁到轻量级锁再到重量级锁)或降级,但JVM的锁优化机制(如HotSpot VM中的锁膨胀机制)会在运行时根据锁的争用情况自动进行这些优化,以提高性能。

6. 注意事项

  • 过度使用 synchronized 可能会导致性能问题,因为它会导致线程阻塞和上下文切换。
  • 应该尽量减小 synchronized 块的范围,只锁定必要的代码段。
  • 在设计多线程程序时,要仔细考虑锁的粒度,以避免死锁和其他并发问题。

synchronized 关键字通过提供监视器锁机制来实现线程同步,确保在同一时刻只有一个线程可以执行某个方法或代码块,从而保护共享资源的一致性和完整性。

volatile关键字在Java中有什么作用?

volatile 关键字在Java中是一个非常重要的关键字,它用于确保变量对所有线程的可见性。当一个变量被声明为 volatile 时,它具备了两个关键特性:

  1. 可见性(Visibility)
    当多个线程访问同一个 volatile 变量时,任何一个线程对这个变量的修改都会立即被其他线程所感知。这是因为当写入 volatile 变量时,JVM 会将修改后的值立即写入主存(Main Memory),并且其他线程在读取该变量时会直接从主存中读取,而不是从自己的工作内存(Working Memory)中读取。这样就保证了变量的可见性。

  2. 禁止指令重排序(Happens-Before)
    volatile 还可以防止指令重排序。在JVM中,为了提高程序的执行效率,编译器和处理器可能会对指令进行重排序,但这种重排序在某些情况下可能会对多线程程序的执行产生影响。volatile 关键字可以确保在 volatile 变量之前的写操作一定会在对该 volatile 变量的读操作之前执行,即保证了一定的“先行发生”(Happens-Before)关系。

然而,需要注意的是,volatile 并不能保证原子性(Atomicity)。原子性是指一个操作是不可中断的,即使在多线程环境下,这个操作一旦开始就不会被其他线程的操作所干扰。例如,对于 volatile int count = 0;count++ 并不是一个原子操作,它实际上包含了三个步骤:读取 count 的值、将值加1、将结果写回 count。在多线程环境下,这三个步骤可能会被其他线程的操作所打断,从而导致数据不一致的问题。

因此,虽然 volatile 提供了变量修改的可见性和一定程度的指令顺序保证,但在需要原子性操作的场合,应该使用 java.util.concurrent.atomic 包下的原子类(如 AtomicInteger)或者使用 synchronized 关键字来保证操作的原子性。

总结来说,volatile 的主要作用是:

  • 确保变量对所有线程的可见性;
  • 禁止指令重排序,保证一定的先行发生关系;
  • 但不保证操作的原子性。
  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值