synchronized工作原理及最佳实践指南

1. synchronized的基本用法与案例分析

1.1. 同步实例方法:对象锁的基本概念

1.1.1. 代码示例:无同步情况下的问题

在没有同步机制的环境下,当多个线程访问同一对象的非同步方法时,会导致资源共享的问题,从而出现数据不一致的现象。

public class Counter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) {
        Counter counter = new Counter();

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

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

        threadA.start();
        threadB.start();

        try {
            threadA.join();
            threadB.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count is: " + counter.getCount());
    }
}

在上述代码中,我们期望最终的count值为20000。但是,由于线程安全问题,实际输出的结果可能会少于20000。

1.1.2. 代码示例:同步实例方法示例

为了解决这个问题,我们可以在increment方法前加上synchronized关键字。

public class Counter {
    private int count = 0;

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

    // 其余代码与之前相同
}

加上synchronized后,每次只有一个线程能持有对象锁,从而避免了并发访问时的数据不一致问题。

1.1.3. 案例分析:对象锁的工作机制

当一个线程访问对象的一个synchronized同步方法时,该线程便持有了该方法所在对象的锁。该锁会保护方法中所有的代码,使得其他线程无法同时执行任何其他的synchronized同步方法。

1.2. 同步静态方法:类锁的应用

1.2.1. 代码示例:静态方法同步

和实例方法锁定对象不同,静态方法同步是锁定的类,也就是.class对象。让我们看一个简单的同步静态方法例子:

public class StaticCounter {
    private static int count = 0;

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

    public static int getCount() {
        return count;
    }

    public static void main(String[] args) {
        Thread threadA = new Thread(StaticCounter::increment);
        Thread threadB = new Thread(StaticCounter::increment);

        threadA.start();
        threadB.start();

        try {
            threadA.join();
            threadB.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final static count is: " + StaticCounter.getCount());
    }
}

在这个例子中,increment是一个同步静态方法,它会锁定StaticCounter.class类对象,确保多线程操作的线程安全。

1.2.2. 案例分析:类锁与对象锁的区别

类锁和对象锁是两个不同的概念。对象锁是每个实例特有的,不同实例之间的对象锁不会相互影响。而类锁是类级别的,所有实例共享同一个类锁,静态同步方法无论被哪个实例调用,都是同步的。

1.3. 同步代码块:精细控制同步

1.3.1. 代码示例:同步代码块用法

我们可以通过同步代码块来控制同步的粒度,以提高效率:

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

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

    // 其余代码与之前相同
}

在这个例子中,我们没有同步整个方法,仅同步了增加count的那一小部分代码。这样,如果increment方法中还有其他逻辑,它们就不会被同步锁影响。

1.3.2. 案例分析:提升性能的同步策略

使用同步代码块而不是同步整个方法可以显著提高应用程序的性能,因为它减少了线程持有锁的时间。不过,选择正确的锁对象和确保所有相关操作都在同步控制之下仍然至关重要。

2. synchronized的工作原理详解

2.1. 深入JVM:synchronized的内部实现

2.1.1. 从字节码视角理解同步

要理解synchronized的工作原理,首先需要从字节码的角度来看。当我们在方法上使用synchronized关键字时,JVM在编译后的字节码中会使用monitorenter和monitorexit指令来实现同步。

public synchronized void syncMethod() {
    // 方法体
}


编译后的字节码中的关键部分看起来是这样的:

0: monitorenter  
// 方法体的字节码
n: monitorexit

当线程进入syncMethod方法时,它将执行monitorenter指令,试图获取对象的监视器锁。如果获取成功,则进入方法体执行;如果获取失败,则该线程进入阻塞状态,直到其他线程释放锁。

2.1.2. 对象头中的Mark Word结构

每个Java对象的对象头中有一部分称为Mark Word,它记录了对象、锁以及垃圾收集相关的信息。当一个线程尝试同步一个对象时,JVM会使用CAS操作(比较并交换)尝试更新这个对象头的Mark Word来获取锁。
如果没有竞争,这个线程将成功地占有锁,并将锁的标记改成指向该线程的锁记录。

2.1.3. 加锁过程的内存语义

synchronized的加锁和解锁过程建立了一个内存屏障,保证了特定操作的顺序性和可见性。简而言之,当线程释放锁时,它之前的操作必须对随后获得同一个锁的其他线程可见。

2.2. 锁的状态变化:偏向锁、轻量级锁与重量级锁

2.2.1. 锁优化:JVM的锁升级过程

为了在不同的竞争情况下提供最佳性能,JVM采用了锁的逐步升级策略,包含偏向锁,轻量级锁及重量级锁。

  • 偏向锁:适用于只有一个线程访问同步块的场景。
  • 轻量级锁:适用于线程交替执行同步块的场景。
  • 重量级锁:适用于多线程同时竞争同步块的场景。

2.2.2. 实战:使用jol(Java Object Layout)工具分析锁状态

我们可以通过jol工具来查看对象在内存中的布局,包括锁的状态。下面是一个简单的使用jol的示例:

import org.openjdk.jol.info.ClassLayout;

public class JOLExample {
    public static void main(String[] args) {
        Object lock = new Object();
        System.out.println(ClassLayout.parseInstance(lock).toPrintable());
    }
}

在这个示例中,我们创建了一个新的Object实例,并打印了它在内存中的布局信息。

2.3. 线程安全机制:监视器模式的实现

2.3.1. Java内存模型(JMM)与线程安全

Java内存模型定义了共享变量的可见性、原子性以及有序性,对实现高效的线程安全机制至关重要。synchronized关键字结合JMM保证了操作的原子性和内存的可见性。

2.3.2. synchronized与wait/notify机制

synchronized另外一个重要的特性是,它可以与Object的wait()和notify()方法结合使用,来实现等待/通知模式。这个模式是多线程协作的一种机制。

public synchronized void waitForCondition() throws InterruptedException {
    while (someConditionIsNotMet()) {
        wait();
    }
    // 对条件满足后的处理
}

public synchronized void notifyConditionChanged() {
    // 更改条件
    notifyAll(); // 或 notify()
}

在waitForCondition方法中,如果某个条件不满足,则调用wait(),使当前线程等待。而在notifyConditionChanged方法中,一旦条件变化,它将通过notifyAll()或notify()唤醒所有/一个在等待的线程。

3. 运行结果与多线程行为剖析

3.1. 线程执行示范:展示同步与非同步的差异

3.1.1. 实验环境搭建与测试代码

我们将通过具体的代码示例来展现同步与非同步的运行结果。这需要构建一个可以模拟多线程竞争条件的测试环境。下面是设置这样一个环境的示例代码:

public class SynchronizedExperiment {
    private int syncCount = 0;
    private int nonSyncCount = 0;

    public synchronized void incrementSync() {
        syncCount++;
    }

    public void incrementNonSync() {
        nonSyncCount++;
    }

    public static void main(String[] args) {
        final SynchronizedExperiment experiment = new SynchronizedExperiment();

        // 启动一定数量的线程同时进行同步和非同步操作
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> experiment.incrementSync()).start();
            new Thread(() -> experiment.incrementNonSync()).start();
        }

        // 等待足够长的时间,确保所有线程操作完成
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("Synchronized count is: " + experiment.syncCount);
        System.out.println("Non-synchronized count is: " + experiment.nonSyncCount);
    }
}

在这个实验中,我们创建了1000个线程执行同步方法incrementSync和1000个线程执行非同步方法incrementNonSync。在没有同步措施的情况下,我们预期nonSyncCount的值小于1000,因为多个线程同时修改同一个变量会导致线程安全问题。

3.1.2. 运行结果解析与问题定位

执行上述程序后,我们通常会发现,同步计数syncCount保持不变,因为synchronized关键字保证了每次只有一个线程能够修改syncCount。然而nonSyncCount可能会小于预期的1000,这是因为多个线程在没有同步的情况下修改同一个变量产生了线程干扰。

3.2. 死锁问题探讨与解决方案

3.2.1. 案例导读:死锁产生的条件

死锁是多线程程序中的一个常见问题,当多个线程在等待对方释放锁,导致所有线程都无法继续执行时就会产生死锁。要发生死锁,以下四个条件必须同时满足:

  • 互斥条件:资源不能被多个线程同时使用。
  • 至少有一个资源被多个线程持有并等待获取更多的资源。
  • 资源不能被线程抢占:线程持有的资源在使用完毕之前不能被其他线程抢占。
  • 循环等待:线程之间形成一种头尾相连的循环等待资源关系。

3.2.2. 避免死锁的设计和开发实践

避免死锁的方法有:

  • 破坏互斥条件:尝试设计算法使多个线程不必互斥地访问资源。
  • 破坏持有和等待条件:可以一次性申请所有资源,避免分步骤获取资源。
  • 破坏不可剥夺条件:如果已经持有资源的线程进一步申请资源时得不到满足,允许它释放已持有的资源。
  • 破坏循环等待条件:给资源编号,只允许以一定顺序申请资源。

通过一系列的代码示例和理论分析,我们演示了synchronized的使用、原理和配合其他语言特性时的行为。在阐述了重要概念并给出了示例之后,读者可以更好地理解如何有效地使用synchronized以及在复杂的多线程环境中如何保证线程安全和性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/601267.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VS调试技巧

1. 什么是bug bug本意是“昆⾍”或“⾍⼦”&#xff0c;现在⼀般是指在电脑系统或程序中&#xff0c;隐藏着的⼀些未被发现的缺陷或 问题&#xff0c;简称程序漏洞。 “Bug” 的创始⼈格蕾丝赫柏&#xff08;Grace Murray Hopper&#xff09;&#xff0c;她是⼀位为美国海军⼯…

C 语言文件输入/输出(I/O)函数大全

C 语言文件输入/输出&#xff08;I/O&#xff09;函数大全 1. fopen() 函数2. fclose() 函数3. fread() 函数4. fwrite() 函数5. fseek() 函数6. ftell() 函数7. rewind() 函数8. feof() 函数9. ferror() 函数10. clearerr() 函数 &#x1f60a; C 语言文件输入/输出&#xf…

gradio图像复原界面改进

图像复原界面展示需要输入图像和复原图像在界面的清晰对比&#xff0c;修改两张图像为同样大小。 默认情况&#xff1a; intreface代码如下&#xff1a; interface gr.Interface(fnrestore, # 要调用的函数inputs[gr.Image(label"输入图像")], # 第一个输入&am…

AI大模型探索之路-训练篇16:大语言模型预训练-微调技术之LoRA

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

测试平台开发:Django开发实战之注册界面实现(上)

实现注册功能&#xff0c;大概包括以下几个步骤 1、设计ui ##字段 通过看数据库里面的user表里面的字段&#xff0c;可以大概知道需要几个字段&#xff1a; emailusernamepasswordpassword_confirm 生成简单的ui界面&#xff0c;复制这个html代码 然后在项目路径下面创建一…

22_Scala集合Seq

文章目录 Seq序列1.构建集合2.List集合元素拼接&&集合拼接3.可变Seq&&List3.1 ListBuffer创建3.2 增删改查3.3 相互转化 Appendix1.Scala起别名2.Seq底层3.关于运算符操作: :4.空集合的表示 Seq序列 –Seq表示有序&#xff0c;数据可重复的集合 1.构建集合 …

整体安全保障服务方案包括哪些方面?

整体安全保障服务方案是一套综合性的措施&#xff0c;旨在保护企业的网络、数据和资源免受各种威胁。主要包含检测、加固、应急保障、安全运营、攻防演练等多项核心能力与服务。 ​安全狗通过专业团队、工具以及专业运营流程&#xff0c;提出了新一代整体安全保障思路&#xff…

开源代码分享(28)-含分布式光伏的配电网集群划分和集群电压协调控制

参考文献&#xff1a; [1] Chai Y , Guo L , Wang C ,et al.Network Partition and Voltage Coordination Control for Distribution Networks With High Penetration of Distributed PV Units[J].IEEE Transactions on Power Systems, 2018:3396-3407.DOI:10.1109/TPWRS.2018…

【深度学习】实验1 波士顿房价预测

波士顿房价预测 代码 import numpy as np import matplotlib.pyplot as pltdef load_data():# 1.从文件导入数据datafile D:\Python\PythonProject\sklearn\housing.datadata np.fromfile(datafile, sep )# 每条数据包括14项&#xff0c;其中前面13项是影响因素&#xff0c…

长方形盒子能容纳定宽的长方形物体最大长度

问题 已知长方形盒子长度a和宽度b&#xff0c;放入一宽度w的长方形物体&#xff0c;求长方形物体最大长度L。 答案 MS Excel公式如下&#xff08;其中B1a&#xff0c;B2b&#xff0c;B3w&#xff09;&#xff1a; L SQRT(B1^2B2^2)-B1*B2*B3*2/(B1^2B2^2)注意 当求得 L ≤…

时间复杂度与空间复杂度(上篇)

目录 前言时间复杂度 前言 算法在运行的过程中要消耗时间资源和空间资源 所以衡量一个算法的好坏要看空间复杂度和时间复杂度&#xff0c; 时间复杂度衡量一个算法的运行快慢 空间复杂度是一个算法运行所需要的额外的空间 一个算法中我们更关心的是时间复杂度 时间复杂度 时…

使用idea管理docker

写在前面 其实idea也提供了docker的管理功能&#xff0c;比如查看容器列表&#xff0c;启动容器&#xff0c;停止容器等&#xff0c;本文来看下如何管理本地的docker daemon和远程的dockers daemon。 1&#xff1a;管理本地 双击shift&#xff0c;录入service&#xff1a; …

24年审计师报名时间汇总所需材料提前准备

2024审计师报名本周开始&#xff08;5月10日起&#xff09;&#xff0c;各地报名时间不一&#xff0c;报名指南整理好了&#xff01; ✅全国报名时间汇总报名费用资格审核&#xff1a;P1~P2。 ✅2024年审计师考试科目&#xff1a; 《审计相关基础知识》和《审计理论与实务》 ✅…

如何创建微信小程序?只需3步完成小程序制作

微信&#xff0c;中国最大的社交媒体应用程序&#xff0c;几个月前推出了微信小程序&#xff0c;这一神奇的功能立即大受欢迎。这些小程序让在中国注册的商业实体所有者创建一个小程序来与微信用户互动。这些小程序不需要在用户手机上进行任何安装&#xff0c;只需通过微信应用…

HP Z620 服务器打开VTx虚拟技术

在使用Virtual Box的时候&#xff0c;虚拟主机启动报错&#xff1a;提示需要VTx。于是到bios里面去设置VTx。 这里有个小坑&#xff0c;就是HP 的bios配置里面&#xff0c;VTx不在常规的“System Configuration”、“Advanced”等地方&#xff0c;而是在“Security”菜单里&…

关于2024年上半年软考考试批次安排的通告

按照《2024年计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试工作安排及有关事项的通知》&#xff08;计考办〔2024〕1号&#xff09;文件精神&#xff0c;结合各地机位实际&#xff0c;现将2024年上半年计算机软件资格考试有关安排通告如下&#xff1a; 一、考…

【排序算法】之冒泡排序

一、算法介绍 冒泡排序&#xff08;Bubble Sort&#xff09;是一种基础的排序算法&#xff0c;它的主要思想是通过重复遍历待排序的列表&#xff0c;比较每对相邻的元素并根据需要交换它们&#xff0c;使得每一遍遍历都能将未排序的最大&#xff08;或最小&#xff09;元素“冒…

RH 414膜电位荧光探针,161433-30-3,具有出色的荧光性质和高度专业化的反应原理

一、试剂信息 名称&#xff1a;RH 414膜电位荧光探针CAS号&#xff1a;161433-30-3结构式&#xff1a; 二、试剂内容 RH 414膜电位荧光探针是一种基于荧光共振能量转移&#xff08;FRET&#xff09;技术的荧光染料&#xff0c;具有出色的荧光性质和高度专业化的反应原理。…

Cordova 12 Android 不支持 http 原因探索

最近在升级 Cordova 到最新版本&#xff0c;升级完成后发现无法请求网络&#xff0c;研究了两次最终发现解决方案。 发现控制台中有日志输出&#xff0c;提示当前是 https &#xff0c;无法直接访问 http。 [INFO:CONSOLE(225)] "Mixed Content: The page at https://lo…

如何更好地使用Kafka? - 运行监控篇

要确保Kafka在使用过程中的稳定性&#xff0c;需要从kafka在业务中的使用周期进行依次保障。主要可以分为&#xff1a;事先预防&#xff08;通过规范的使用、开发&#xff0c;预防问题产生&#xff09;、运行时监控&#xff08;保障集群稳定&#xff0c;出问题能及时发现&#…
最新文章