深入理解Java中synchronized三种使用方式:助您写出线程安全的代码

一、概念

synchronized 是一种内置的 Java 关键字,它用于实现线程的同步。当一个线程进入synchronized块或方法时,它获得了锁,这会阻止其他线程同时进入相同的synchronized块或方法,从而确保了共享资源的互斥访问。

synchronized 是 Java 中用于实现线程同步的关键字。它提供了一种独占锁的机制,用于确保多个线程之间的互斥访问共享资源。以下是关于synchronized的更详细解释:

使用方法:

synchronized关键字在Java中有三种主要的使用方式:

  1. 修饰非静态方法(锁class的同一实例的此方法)

当您在实例的非静态方法上使用synchronized关键字时,它会将该方法变成同步方法,相当于对当前实例对象(this)加锁,this作为对象监视器。这意味着只有一个线程可以同时执行该实例方法,以确保对该实例的互斥访问。 当前类会创建多个实例对象,synchronized独立的控制每个实例对象的同步。

public synchronized void synchronizedInstanceMethod() {
    // 同步的代码块
}
  1. 修饰静态方法(锁class的所有实例的此方法)

在静态方法上使用synchronized关键字时,它会将该方法变为同步静态方法,相当于对当前类的Class对象加锁,当前类的Class对象作为对象监视器。这意味着只有一个线程可以同时执行该静态方法,以确保对该类的互斥访问。 当前类会创建多个实例对象,所以实例对应同一个静态方法,所以synchronized控制所以实例对象的同步。锁定的是类的 Class 对象,因此它会阻止不同实例以及静态方法之间的并发执行,因为它们共享相同的 Class 对象。

public static synchronized void synchronizedStaticMethod() {
    // 同步的代码块
}
  1. 修饰代码块(锁定特定的对象)

可以使用synchronized关键字来创建同步代码块,这样可以指定要加锁的对象,括号中括起来的对象就是对象监视器。这允许更细粒度的控制,可以选择对某个特定对象进行同步,而不是整个方法或类。

方式一:

当你使用 synchronized(class) 时,你锁定的是整个类的对象,而不是实例对象。这意味着无论多少实例对象存在,它们都会竞争同一个锁。

Object lock = new Object();
synchronized (lock) {
    // 同步的代码块
}

方式二:

当你使用 synchronized(this) 时,你锁定的是当前实例对象(this)。这意味着同一实例的不同方法调用会相互排斥,但不同实例之间的方法调用不会相互排斥。

synchronized (this) {
    // 同步的代码块
}

二、实现原理

1、实现原理

synchronized底层原理是基于JVM的指令和对象的监视器(monitor)来实现的。synchronized可以修饰方法或者代码块,用来保证线程的同步和安全。

当一个线程要执行一个被synchronized修饰的方法或代码块时,它需要先获取该方法或代码块所属对象的监视器。如果获取成功,那么该线程就可以执行同步代码,并且监视器的计数器加一。如果获取失败,那么该线程就会阻塞,直到监视器被释放。

当一个线程执行完同步代码后,它会释放监视器,并且监视器的计数器减一。如果计数器为零,那么说明没有线程持有该监视器,其他线程就可以竞争获取该监视器。

synchronized修饰方法时,在字节码层面会有一个ACC_SYNCHRONIZED标志,用来表示该方法是同步的。synchronized修饰代码块时,在字节码层面会有monitorenter和monitorexit两个指令,分别用来进入和退出监视器。

synchronized 的底层实现原理可以概括为以下几点:

  • synchronized 通过监视器锁来实现线程同步。
  • 每个 Java 对象都有一个监视器锁。
  • 线程在获取了对象的监视器锁后,可以执行被修饰的代码。
  • 线程在释放了对象的监视器锁后,其他线程可以尝试获取监视器锁。

2、JDK底层的优化

synchronized在JDK1.6之后进行了优化,引入了偏向锁,轻量级锁,自旋锁等概念,用来提高性能和减少阻塞开销。以下是一些常见的优化详细说明:

  1. 偏向锁:JDK引入了偏向锁,它会将锁定的对象与线程相关联,当一个线程获得锁时,它会标记对象为已偏向该线程,以后再次进入同步块时,不需要竞争锁,而是直接获得。这对于减少无竞争情况下的锁开销非常有用。
  2. 轻量级锁:在低竞争情况下,JDK使用轻量级锁来减小锁开销。轻量级锁采用自旋方式来等待锁的释放,而不是进入阻塞状态。
  3. 自旋锁:当轻量级锁尝试获取锁失败时,JDK可以选择使用自旋锁。自旋锁不会使线程进入阻塞状态,而是一直尝试获取锁,通常在短时间内完成。这对于低竞争锁非常有用。
  4. 适应性自旋:JDK中的锁可以根据历史性能数据来调整自旋等待的次数,以达到更好的性能。

这些优化措施有助于提高synchronized的性能,使其在不同的竞争场景中更加高效。但请注意,优化是基于JVM和硬件平台的,因此在不同的环境中表现可能会有所不同。

三、相关题目

问题1:synchronized 是什么?它的作用是什么?

答案:synchronized 是Java中的关键字,用于实现多线程同步。它的主要作用是确保在同一时刻只有一个线程可以访问被synchronized修饰的代码块或方法,以避免多线程之间的竞态条件和数据不一致问题。

问题2:synchronized 有哪些用法?

答案:synchronized 可以用于以下用法:

  • 同步非静态实例方法:synchronized 修饰非静态方法,锁定的是实例对象。
  • 同步静态方法:synchronized 修饰静态方法,锁定的是类对象。
  • 同步代码块:synchronized 修饰代码块,可以手动指定锁对象,灵活控制同步范围。

问题3:什么是锁对象?

答案:锁对象是在synchronized代码块或方法中用于实现同步的对象。对于同步实例方法,锁对象是实例对象本身(this),而对于同步静态方法,锁对象是类的Class对象。对于同步代码块,你可以手动指定锁对象。

问题4:什么是重入锁?

答案:重入锁是指当一个线程持有锁时,它可以多次进入被锁定的代码块或方法而不会被阻塞。Java中的synchronized是可重入锁的一个例子,这意味着同一线程可以多次获取同一个锁,而不会造成死锁。

问题5:什么是偏向锁?

答案:偏向锁是一种优化,用于减少锁竞争的开销。它允许线程在没有竞争的情况下快速获得锁,而不必像重量级锁那样进入阻塞状态。只有在多线程竞争锁时,偏向锁才会升级为重量级锁。

问题6:什么是自旋锁?

答案:自旋锁是一种轻量级锁,它在尝试获取锁时不会立即阻塞线程,而是会在循环中不断尝试获取锁。这可以减少线程阻塞和恢复的开销,但如果锁竞争激烈,自旋锁可能会浪费大量CPU时间。

问题7:什么是锁粗化?

答案:锁粗化是一种优化,它将多个连续的synchronized块合并成一个更大的同步块,减少了锁操作的开销。这可以避免频繁地获取和释放锁,提高性能。

问题8:什么是锁消除?

答案:锁消除是一种优化,它通过静态分析来检测某些锁是不必要的,然后将其删除,以提高程序的性能。这通常发生在编译器级别。

问题9:synchronized 与 volatile 有什么区别?

答案:synchronized 用于实现多线程同步,确保线程之间的可见性和原子性。volatile 用于确保变量的可见性,但不能实现原子性。synchronized 具有更广泛的用途,而 volatile 主要用于标记变量以确保可见性。

猜你喜欢

转载自blog.csdn.net/citywu123/article/details/134303906
今日推荐