【Java原理系列】 Java synchronized 关键字的原理示例详解

本文详细解释了Java中`synchronized`关键字的原理、如何在代码中使用示例,以及与`lock`关键字的异同,强调了其在线程同步中的重要性和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java synchronized 关键字的原理用法详解

原理

synchronized 是 Java 中的关键字,用于实现线程之间的同步。它可以应用于方法、代码块或静态方法上。

  • synchronized 修饰方法时,它锁住的是整个方法体,即使方法内部有多个同步块,也会形成一个锁。
  • synchronized 修饰代码块时,它只锁住代码块中的部分代码。
  • synchronized 修饰静态方法时,它锁住的是类的 Class 对象。

synchronized 的原理是使用对象级别的锁(也称为监视器锁)来实现同步。每个对象都有一个与之关联的监视器锁,当线程进入被 synchronized 修饰的代码块时,它将尝试获取该对象的监视器锁,如果锁已被其他线程持有,则线程将被阻塞直到获取到锁为止。一旦线程执行完 synchronized 代码块中的代码,它会释放监视器锁,以便其他线程可以获取锁并执行。

synchronized 的主要作用是确保共享资源在多线程环境下的安全访问,避免出现数据竞争和不一致的问题。

示例

以下是一个使用 synchronized 关键字的示例:

public class Counter {
    private int count;

    public Counter() {
        count = 0;
    }

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

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

    public synchronized int getCount() {
        return count;
    }
}

在上述示例中,我们定义了一个 Counter 类,其中的三个方法 increment()decrement()getCount() 都使用了 synchronized 关键字修饰。这意味着这些方法在同一时间只能被一个线程访问。

假设有两个线程同时对 Counter 对象进行操作:

Counter counter = new Counter();

Thread thread1 = new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        counter.increment();
    }
});

Thread thread2 = new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        counter.decrement();
    }
});

thread1.start();
thread2.start();

// 等待两个线程执行完毕
try {
    thread1.join();
    thread2.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

System.out.println("Final Count: " + counter.getCount());

在上述示例中,我们创建了两个线程,分别执行 increment()decrement() 方法,对 Counter 对象的计数进行增加和减少操作。由于这些方法都是使用 synchronized 关键字修饰的,因此每次只能有一个线程可以访问它们。最终输出的计数结果应为 0,因为增加和减少操作的数量相等。

通过使用 synchronized 关键字,我们确保了对共享资源的安全访问,避免了多线程环境下可能出现的竞态条件和数据不一致的问题。

注意事项

在使用 synchronized 关键字时,需要注意以下几点:

  • synchronized 的范围应该尽量小,避免锁住过多的代码,以提高程序的并发性能。
  • 避免在 synchronized 代码块内部进行耗时的操作,这可能会导致其他线程长时间等待。
  • 当使用 synchronized 关键字时,要确保所有访问共享资源的方法都被同步,否则仍然可能出现线程安全问题。
  • 尽量使用更高级别的同步机制,

和lock关键字异同

synchronizedlock 都是 Java 中用于实现线程同步的机制,它们有一些联系和区别。

联系:

  • 目标相同:都是为了在多线程环境下保证共享资源的安全访问,避免数据竞争和不一致的问题。
  • 同步机制:都可以确保同一时间只有一个线程能够访问被保护的代码块或方法。
  • 阻塞特性:当一个线程获取到锁(或者进入 synchronized 代码块)时,其他线程会被阻塞,直到锁被释放(或者线程退出 synchronized 代码块)。

区别:

  • 锁的获取方式:synchronized 是隐式获取锁,由 JVM 自动管理;而 lock 是显示获取锁,需要手动调用 lock() 方法来获取锁,并在合适的地方调用 unlock() 方法释放锁。
  • 可重入性:synchronized 是可重入锁,同一个线程可以重复获取同一个锁;而 lock 也是可重入锁,但需要手动控制锁的获取和释放次数。
  • 粒度灵活性:synchronized 的粒度较粗,通常是对整个方法或代码块进行加锁;而 lock 提供了更细粒度的锁定,可以根据需求灵活地控制锁的范围。
  • 锁的可中断性:lock 提供了 tryLock() 方法,可以尝试获取锁,如果锁已经被其他线程持有,则立即返回,而不是一直等待;而 synchronized 则无法中断一个正在等待锁的线程,只能等待锁的释放。
  • 性能差异:由于 lock 提供了更细粒度的控制和高级特性(如条件变量),相比之下在高并发情况下可以更好地控制线程的调度和资源消耗,因此在某些情况下可能具有更好的性能。

总结起来,synchronized 是一种更简单易用的线程同步机制,适合大多数情况;而 lock 则提供了更高级、更灵活的同步控制能力,适合需要更细粒度控制和更高性能要求的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值