一篇文章讲清楚 `synchronized` 关键字的作用及原理,你一定能听懂。

365bet怎么样 🌸 2025-11-14 00:51:22 🎨 admin 👁️ 3271 ❤️ 490
一篇文章讲清楚 `synchronized` 关键字的作用及原理,你一定能听懂。

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE相关知识点了,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~

🏆本文收录于「滚雪球学Java」专栏中,这个专栏专为有志于提升Java技能的你打造,覆盖Java编程的方方面面,助你从零基础到掌握Java开发的精髓。赶紧关注,收藏,学习吧!

代码语言:java复制环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8前言在 Java 的多线程编程中,数据同步和并发控制是开发者面临的重要问题之一。随着系统变得越来越复杂,高并发操作已成为现代应用程序中的常见场景。为了确保共享资源在多线程环境下不会被同时修改,Java 提供了多种并发控制机制,其中最基本、最常用的就是 synchronized 关键字。

synchronized 是一种内置的同步机制,用来解决多线程访问共享资源时的线程安全问题。它的主要作用是确保同一时刻只有一个线程能够访问某些特定的代码片段或方法。为了帮助你深入理解 synchronized 的用法和原理,这篇文章将从多个角度讲解它的具体作用、使用场景、工作机制,并通过丰富的示例演示它在实际开发中的应用。

一、为什么需要同步?在多线程环境中,当多个线程同时访问共享资源(如全局变量、对象字段)时,可能会出现数据不一致的情况。假设两个线程同时读取和修改同一个变量值,若没有恰当的同步控制,最终的结果可能并不如预期,产生所谓的竞态条件(Race Condition)。这种情况通常是程序设计中的严重错误,会导致数据错误、程序崩溃等问题。

示例一:没有同步的多线程问题代码语言:java复制class Counter {

private int count = 0;

// 普通方法,没有同步控制

public void increment() {

count++;

}

public int getCount() {

return count;

}

}

public class NoSyncExample {

public static void main(String[] args) throws InterruptedException {

Counter counter = new Counter();

// 创建两个线程同时对计数器进行1000次增量操作

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.increment();

}

});

thread1.start();

thread2.start();

thread1.join();

thread2.join();

// 预期计数结果应为 2000,但可能会出现小于2000的结果

System.out.println("计数结果: " + counter.getCount());

}

}输出结果:

代码语言:json复制计数结果: 1870 (结果可能会变化)在这个示例中,Counter 类中的 increment() 方法并没有做任何同步处理,因此多个线程可能会同时读取和修改 count,导致最终结果比预期的 2000 小。这是由于线程竞态问题引起的,并发操作中的共享资源在没有适当同步控制时会产生错误的结果。

为了解决这种问题,Java 提供了 synchronized 关键字,用以确保同一时间只有一个线程能够访问共享资源。

二、synchronized 关键字的作用synchronized 关键字的作用是使某个方法或代码块变为临界区(critical section),从而避免线程同时执行这些代码。其基本作用体现在以下两个方面:

线程互斥访问:只有一个线程能够执行带有 synchronized 关键字的方法或代码块,其他线程必须等待,直到锁被释放。可见性保障:当一个线程执行完同步代码并释放锁时,其他线程可以看到这个线程对共享变量的修改结果,保证了数据的最新状态在所有线程之间可见。通过 synchronized,我们可以确保多线程操作的正确性,防止竞态条件的发生。接下来我们将介绍 synchronized 的具体用法。

三、synchronized 的使用方式synchronized 主要有两种使用方式:同步方法和同步代码块。

1. 同步方法同步方法是最简单的 synchronized 使用方式,通过在方法声明中加上 synchronized 关键字,即可确保该方法在多线程环境中是线程安全的。

示例二:同步方法的使用代码语言:java复制class Counter {

private int count = 0;

// 通过 synchronized 关键字修饰方法,确保线程安全

public synchronized void increment() {

count++;

}

public int getCount() {

return count;

}

}

public class SynchronizedMethodExample {

public static void main(String[] args) throws InterruptedException {

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.increment();

}

});

thread1.start();

thread2.start();

thread1.join();

thread2.join();

System.out.println("计数结果: " + counter.getCount());

}

}输出结果:

代码语言:json复制计数结果: 2000在这个例子中,increment() 方法通过 synchronized 关键字修饰,确保了两个线程同时操作时,只有一个线程可以执行该方法,避免了竞态条件,保证了最终结果的正确性。

2. 同步代码块有时候,我们不需要对整个方法进行同步,只需对其中的某个代码片段进行同步处理即可。这时,可以使用同步代码块,它允许我们对需要同步的代码进行精细控制。

示例三:同步代码块的使用代码语言:java复制class Counter {

private int count = 0;

public void increment() {

// 使用同步代码块进行线程安全控制

synchronized (this) {

count++;

}

}

public int getCount() {

return count;

}

}

public class SynchronizedBlockExample {

public static void main(String[] args) throws InterruptedException {

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.increment();

}

});

thread1.start();

thread2.start();

thread1.join();

thread2.join();

System.out.println("计数结果: " + counter.getCount());

}

}输出结果:

代码语言:json复制计数结果: 2000同步代码块允许我们仅对关键的代码部分进行同步,而不必同步整个方法。这种方式可以提高代码的执行效率,尤其是在大部分代码不涉及共享资源时。

3. 静态同步方法如果方法是静态的,即属于类而非对象实例,synchronized 将作用于该类的类级别锁(Class Level Lock),意味着该方法对于该类的所有实例都是同步的。

示例四:静态同步方法代码语言:java复制class Counter {

private static int count = 0;

// 静态同步方法,锁作用于类

public static synchronized void increment() {

count++;

}

public static int getCount() {

return count;

}

}

public class SynchronizedStaticMethodExample {

public static void main(String[] args) throws InterruptedException {

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.increment();

}

});

thread1.start();

thread2.start();

thread1.join();

thread2.join();

System.out.println("计数结果: " + Counter.getCount());

}

}输出结果:

代码语言:json复制计数结果: 2000在这种情况下,静态同步方法 increment() 使用类级别的锁,因此即使是多个线程,也可以确保数据的线程安全。

四、synchronized 的原理synchronized 的底层原理是依赖于 JVM 的对象监视器锁(Monitor)机制。每个 Java 对象都有一个内置的锁,当一个线程执行 synchronized 方法或代码块时,它必须首先获得该对象的锁。一旦线程获得锁,其他试图访问同一代码块的线程将被阻塞,直到该线程释放锁。

在 Java 字节码层面,synchronized 是通过 monitorenter 和 monitorexit 指令实现的:

monitorenter:当线程进入同步块或同步方法时,JVM 会执行 monitorenter 指令,获取该对象的监视器锁。monitorexit:当线程退出同步块或同步方法时,JVM 执行 monitorexit 指令,释放监视器锁。此外,JVM

对 synchronized 进行了多层优化,包括偏向锁、轻量级锁和重量级锁,这些机制极大地提高了锁的性能,避免了传统锁机制的高开销。

锁的优化机制偏向锁:当只有一个线程竞争时,锁会偏向该线程,减少锁的获取和释放开销。轻量级锁:如果存在多个线程竞争但并未发生实际的冲突,则可以使用轻量级锁来减少上下文切换和性能开销。重量级锁:当线程竞争激烈时,系统会升级到重量级锁来确保线程安全。五、synchronized 的优缺点优点简单易用:相比于其他复杂的并发控制机制,synchronized 使用起来非常直观,开发者只需要在方法或代码块前加上 synchronized 关键字即可。线程安全:synchronized 能够有效防止竞态条件,确保多线程访问共享资源时数据的一致性。可见性保障:通过内置的内存屏障,synchronized 确保了一个线程对共享数据的修改在释放锁后对其他线程可见。缺点性能开销:尽管 JVM 对 synchronized 进行了多种优化,但在高并发场景下,过多的锁竞争仍然会带来性能损耗,特别是在锁争夺激烈的情况下,容易引发线程上下文切换和资源等待。阻塞机制:synchronized 是一种阻塞同步机制,一旦某个线程持有锁,其他线程必须等待,容易造成线程饥饿或系统响应缓慢。六、synchronized 与其他并发机制对比synchronized 是 Java 提供的基本并发控制机制,但在高并发场景中,Java 的并发包(java.util.concurrent)还提供了更高级的同步工具,如 ReentrantLock 和 Atomic 类。

ReentrantLock:是 synchronized 的高级替代方案,它提供了更加灵活的锁机制,如公平锁、可中断锁等,并且允许锁在不释放的情况下被中断或尝试获取。Atomic 类:如 AtomicInteger、AtomicLong 等类,通过无锁的 CAS(Compare-And-Swap)机制来保证并发操作的原子性,适用于高并发、低锁竞争的场景。七、总结synchronized 关键字在 Java 多线程编程中扮演着重要角色,能够有效地解决线程同步问题,确保多线程操作的安全性。本文从 synchronized 的作用、使用方式、底层原理到锁的优化机制进行了详细解析,并通过多个实例演示了它的实际应用。

尽管 synchronized 简单易用,但在高并发场景中,开发者需要根据实际情况选择合适的并发控制工具,平衡性能和复杂性。无论你是初学者还是有经验的开发者,掌握好 synchronized 关键字是编写高效并发程序的第一步。

扩展阅读:

Java 并发编程最佳实践JVM 内存模型与锁优化☀️建议/推荐你 无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学Java」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门Java编程,就像滚雪球一样,越滚越大,指数级提升。

码字不易,如果这篇文章对你有所帮助,帮忙给bug菌来个一键三连(关注、点赞、收藏) ,您的支持就是我坚持写作分享知识点传播技术的最大动力。

同时也推荐大家关注我的硬核公众号:「猿圈奇妙屋」 ;以第一手学习bug菌的首发干货,不仅能学习更多技术硬货,还可白嫖最新BAT大厂面试真题、4000G Pdf技术书籍、万份简历/PPT模板、技术文章Markdown文档等海量资料,你想要的我都有!

📣关于我 我是bug菌,CSDN | 掘金 | infoQ | 51CTO 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,掘金等平台签约作者,华为云 | 阿里云| 腾讯云等社区优质创作者,全网粉丝合计30w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。

--End

相关推荐

华硕E502系列
365完美体育app官网

华硕E502系列

📅 08-08 👁️ 7452
【帮派建议】小建议-建设帮派仓库
365bet官网体育

【帮派建议】小建议-建设帮派仓库

📅 09-20 👁️ 288
趣分期官网[编辑]
365完美体育app官网

趣分期官网[编辑]

📅 09-30 👁️ 658