在多线程编程中,同步是一个至关重要的概念。Java 提供了多种机制来实现线程间的同步,其中 synchronized 关键字是最常用且最基础的工具之一。它能够确保同一时刻只有一个线程访问共享资源,从而避免数据竞争和不一致的问题。本文将深入探讨 synchronized 关键字的底层原理、具体作用以及多种用法,帮助读者全面理解这一核心特性。
同步的概念
定义:同步是指在同一时刻只允许一个线程访问共享资源的操作。
目的:防止多个线程同时修改同一个变量,导致数据不一致或程序崩溃。
Synchronized 的工作机制
锁机制:synchronized 使用对象锁来实现线程同步。
锁的类型:内置锁:每个对象都有一个内置锁(也称为监视器锁)。
可重入锁:支持同一个线程多次获取同一把锁。
示例:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
解释:
increment() 和 getCount() 方法都被标记为 synchronized,这意味着每次只能有一个线程进入这些方法。
Monitor 锁
Monitor:Java 中的每个对象都有一个与之关联的监视器(Monitor),用于控制线程的访问。
工作流程:当线程调用 synchronized 方法时,它会尝试获取对象的锁。
如果锁已被其他线程持有,则当前线程会被阻塞,直到锁可用。
获取锁后,线程可以执行同步块或方法。
执行完毕后,线程释放锁。
示例:
public class SynchronizedExample {
private final Object lock = new Object();
public void methodA() {
synchronized (lock) {
// 临界区代码
}
}
}
解释:
使用 synchronized 块显式地指定锁对象,确保只有持有 lock 对象的线程才能进入临界区。
保证线程安全
场景:当多个线程访问共享资源时,确保操作的原子性。
示例:
public class SharedResource {
private int value = 0;
public synchronized void setValue(int newValue) {
value = newValue;
}
public synchronized int getValue() {
return value;
}
}
解释:
setValue() 和 getValue() 方法被同步,确保同一时刻只有一个线程能够修改或读取 value。
防止死锁
死锁:两个或多个线程互相等待对方释放锁,导致程序无法继续执行。
示例:
public class DeadlockExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void thread1() {
synchronized (lock1) {
System.out.println("Thread 1 holding lock 1...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
System.out.println("Thread 1 waiting for lock 2...");
synchronized (lock2) {
System.out.println("Thread 1 holding lock 1 & 2...");
}
}
}
public void thread2() {
synchronized (lock2) {
System.out.println("Thread 2 holding lock 2...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
System.out.println("Thread 2 waiting for lock 1...");
synchronized (lock1) {
System.out.println("Thread 2 holding lock 1 & 2...");
}
}
}
}
解释:
thread1() 和 thread2() 分别获取 lock1 和 lock2,可能导致死锁。
提高性能
细粒度锁:通过显式指定锁对象,减少不必要的锁范围。
示例:
public class FineGrainedLocking {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void operation1() {
synchronized (lock1) {
// 临界区代码
}
}
public void operation2() {
synchronized (lock2) {
// 临界区代码
}
}
}
解释:
将锁范围缩小到最小必要区域,提高并发性能。
方法级别的同步
修饰符:synchronized 可以直接修饰类的方法。
示例:
public class SyncMethodExample {
public synchronized void methodA() {
// 临界区代码
}
public synchronized void methodB() {
// 临界区代码
}
}
解释:
methodA() 和 methodB() 都被同步,同一时刻只有一个线程可以访问它们。
块级别的同步
语法:
synchronized (object) { ... }
示例:
public class SyncBlockExample {
private final Object lock = new Object();
public void method() {
synchronized (lock) {
// 临界区代码
}
}
}
解释:
显式指定锁对象,提高灵活性。
静态方法同步
特性:静态方法同步锁的是类本身,而不是实例。
示例:
public class StaticSyncExample {
public static synchronized void staticMethod() {
// 临界区代码
}
}
解释:
staticMethod() 同步锁的是 StaticSyncExample.class。
同步代码块与锁对象
特点:通过自定义锁对象实现更细粒度的同步。
示例:
public class CustomLockExample {
private final Object lock = new Object();
public void method() {
synchronized (lock) {
// 临界区代码
}
}
}
解释:
使用外部定义的锁对象,而非默认的 this。
优点
简单易用:无需手动管理锁,语法简洁。
自动管理:进入和退出同步块时自动获取和释放锁。
线程安全:确保共享资源的线程安全性。
缺点
性能问题:锁的获取和释放会带来一定的开销。
死锁风险:不当使用可能导致死锁。
粒度较粗:无法实现更细粒度的锁控制。
通过本文的学习,我们深入了解了 synchronized 关键字在 Java 中的核心作用及其背后的原理。无论是保证线程安全、防止死锁,还是提高性能,synchronized 都提供了简单而有效的解决方案。然而,在实际应用中,开发者需要注意其潜在的性能瓶颈和死锁风险,合理选择锁的粒度和范围。希望本文的内容能为你的 Java 编程之路提供有力的支持,让你在处理多线程问题时更加得心应手!
声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com
验证银行卡、身份证、姓名、手机号是否一致并返回账户类型
支持全球约2.4万个城市地区天气查询,如:天气实况、逐日天气预报、24小时历史天气等
支持识别各类商场、超市及药店的购物小票,包括店名、单号、总金额、消费时间、明细商品名称、单价、数量、金额等信息,可用于商品售卖信息统计、购物中心用户积分兑换及企业内部报销等场景
涉农贷款地址识别,支持对私和对公两种方式。输入地址的行政区划越完整,识别准确度越高。
根据给定的手机号、姓名、身份证、人像图片核验是否一致