网站别人给我做的备案 我能更改吗公司装修费属于什么费用

张小明 2026/1/9 11:34:43
网站别人给我做的备案 我能更改吗,公司装修费属于什么费用,wordpress 年份索引,公司三站合一的网站在Java并发编程中#xff0c;线程安全是永恒的核心话题。当多个线程同时访问共享资源时#xff0c;很容易出现数据不一致、脏数据等问题。而synchronized关键字作为Java内置的同步机制#xff0c;是解决线程安全问题的基础手段。本文将从线程安全本质出发#xff0c;逐步拆…在Java并发编程中线程安全是永恒的核心话题。当多个线程同时访问共享资源时很容易出现数据不一致、脏数据等问题。而synchronized关键字作为Java内置的同步机制是解决线程安全问题的基础手段。本文将从线程安全本质出发逐步拆解synchronized的使用场景、底层实现原理、锁升级机制并用生产者-消费者模型实战验证帮助大家彻底掌握这一核心技术。一、线程安全的核心为什么需要Synchronized在深入synchronized之前我们首先要明白为什么多线程环境下会出现安全问题1.1 线程安全的定义线程安全是指多个线程并发访问某个Java对象时无论操作系统如何调度线程、如何交替执行该对象都能表现出一致的、正确的行为最终结果与单线程执行完全相同。1.2 线程不安全的根源三个核心概念要理解线程不安全必须先掌握以下三个关键概念临界区资源可以被多个线程共享访问的资源如共享变量、数据库连接、文件等。临界区代码段每个线程中访问临界资源的那段代码比如自增运算、修改共享变量的逻辑。竞态条件多个线程在临界区代码段并发执行时由于代码执行顺序不同导致最终结果不确定的情况。1.3 经典案例自增运算的线程不安全最典型的线程不安全场景就是自增运算i它看似是一个原子操作实则包含三个独立的步骤从主内存读取变量i的值到线程工作内存内存取值在线程工作内存中对i进行加1操作寄存器计算将计算后的结果写回主内存存值到内存。当多个线程同时执行i时就可能出现指令交错线程A读取i0还未完成加1线程B也读取i0执行加1后写回主内存i1线程A继续执行加1此时工作内存中还是0写回主内存i1。最终两次自增只得到了1这就是竞态条件导致的线程不安全。而synchronized的核心作用就是保证临界区代码段的原子性执行——同一时间只有一个线程能进入临界区避免指令交错。二、Synchronized的使用方式同步块与同步方法synchronized的使用非常灵活主要分为同步块和同步方法两大类核心区别在于锁对象的不同。2.1 同步块Synchronized Block同步块是指用synchronized关键字包裹的代码块语法格式synchronized(锁对象){// 临界区代码段}锁对象要求必须是一个普通的Object对象如new Object()、自定义对象实例不能是基本数据类型如int、long。核心原理线程进入同步块前必须先获取锁对象的监视锁内置锁执行完代码块后自动释放锁。优点粒度更细只锁定临界区代码不影响其他代码的执行效率。示例用同步块解决自增线程安全问题publicclassSafeCounter{privateintcount0;// 锁对象建议使用专门的锁对象避免与其他用途冲突privatefinalObjectlocknewObject();publicvoidincrement(){synchronized(lock){// 锁定临界区count;}}publicintgetCount(){returncount;}}2.2 同步方法Synchronized Method同步方法是指在方法声明时添加synchronized关键字语法格式// 普通同步方法publicsynchronizedvoidmethod(){// 临界区代码}// 静态同步方法publicstaticsynchronizedvoidstaticMethod(){// 临界区代码}同步方法的锁对象是隐式指定的分为两种情况普通同步方法锁对象是当前对象this即调用该方法的对象实例。静态同步方法锁对象是当前类的Class对象如SafeCounter.class因为静态方法属于类不属于某个实例。注意事项普通同步方法和静态同步方法的锁对象不同因此它们之间不会相互阻塞比如一个线程执行普通同步方法另一个线程执行静态同步方法不会竞争锁。同步方法的粒度较粗会锁定整个方法体如果方法中包含非临界区代码会降低执行效率。示例同步方法实现自增安全publicclassSafeCounter{privateintcount0;// 普通同步方法锁对象是thispublicsynchronizedvoidincrement(){count;}// 静态同步方法锁对象是SafeCounter.classpublicstaticsynchronizedvoidstaticIncrement(SafeCountercounter){counter.count;}}三、实战用Synchronized实现生产者-消费者模型生产者-消费者模型是并发编程中的经典场景恰好能体现synchronized的同步能力同时需要结合wait()和notify()实现线程间通信。3.1 模型核心需求并发安全生产者与生产者、消费者与消费者、生产者与消费者之间并发操作缓冲区不能出现数据不一致如重复生产、重复消费、数据丢失。边界控制缓冲区满时生产者不能继续生产缓冲区空时消费者不能继续消费。资源优化空闲线程如缓冲区满的生产者、缓冲区空的消费者应阻塞而非空循环避免浪费CPU。3.2 实现思路缓冲区用队列Queue存储数据作为临界区资源。同步机制用synchronized锁定缓冲区保证生产/消费操作的原子性。线程通信用wait()让线程阻塞并释放锁和notifyAll()唤醒所有阻塞线程实现边界控制。3.3 完整代码实现importjava.util.LinkedList;importjava.util.Queue;// 数据缓冲区临界区资源classDataBuffer{privatefinalQueueIntegerbuffernewLinkedList();privatefinalintMAX_CAPACITY10;// 缓冲区最大容量// 生产者生产数据publicsynchronizedvoidproduce(intdata)throwsInterruptedException{// 缓冲区满时生产者阻塞while(buffer.size()MAX_CAPACITY){System.out.println(缓冲区满生产者Thread.currentThread().getName()阻塞);wait();// 释放锁进入阻塞状态}// 生产数据buffer.offer(data);System.out.println(生产者Thread.currentThread().getName()生产data当前缓冲区大小buffer.size());notifyAll();// 唤醒所有阻塞的线程可能是消费者或其他生产者}// 消费者消费数据publicsynchronizedintconsume()throwsInterruptedException{// 缓冲区空时消费者阻塞while(buffer.isEmpty()){System.out.println(缓冲区空消费者Thread.currentThread().getName()阻塞);wait();// 释放锁进入阻塞状态}// 消费数据intdatabuffer.poll();System.out.println(消费者Thread.currentThread().getName()消费data当前缓冲区大小buffer.size());notifyAll();// 唤醒所有阻塞的线程returndata;}}// 生产者线程classProducerimplementsRunnable{privatefinalDataBufferbuffer;publicProducer(DataBufferbuffer){this.bufferbuffer;}Overridepublicvoidrun(){for(inti0;i20;i){// 生产20个数据try{buffer.produce(i);Thread.sleep(100);// 模拟生产耗时}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}}}// 消费者线程classConsumerimplementsRunnable{privatefinalDataBufferbuffer;publicConsumer(DataBufferbuffer){this.bufferbuffer;}Overridepublicvoidrun(){for(inti0;i20;i){// 消费20个数据try{buffer.consume();Thread.sleep(200);// 模拟消费耗时}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}}}3.4 关键说明为什么用while而非if判断边界wait()可能被虚假唤醒即没有被notify()唤醒而是系统自发唤醒while会重新检查条件确保线程只有在满足条件时才继续执行。notifyAll()vsnotify()notify()只唤醒一个随机阻塞的线程可能导致某些线程长期阻塞如生产者一直唤醒生产者notifyAll()唤醒所有阻塞线程确保公平性因此在生产者-消费者模型中更常用。synchronized与wait()/notify()的关系wait()和notify()必须在synchronized代码块/方法中调用否则会抛出IllegalMonitorStateException。因为这两个方法需要操作对象的监视器锁必须先获取锁才能操作。四、底层原理Java对象结构与内置锁要真正理解synchronized必须深入其底层实现——它依赖于Java对象的内置锁监视器锁Monitor而内置锁的实现又与Java对象结构密切相关。4.1 Java对象的内存结构4.1.1 核心部分对象头Object Header对象头是实现synchronized的关键其中最重要的是Mark Word和Class PointerMark Word标记字占8字节64位用于存储对象的线程锁状态、哈希码、GC分代年龄等信息。其结构会随着锁状态的变化而变化后面锁升级会详细讲。Class Pointer类型指针占8字节指向方法区中该对象对应的Class元数据如类名、方法、字段等JVM通过它确认对象的类型。Array Length数组长度仅数组对象有占4字节存储数组的长度。4.1.2 对象体与对齐字节对象体存储对象的成员变量值如count、name等变量的类型和顺序会影响内存占用。对齐字节JVM要求对象的总大小必须是8字节的整数倍内存对齐这样CPU访问内存时效率更高。如果对象头对象体的大小不是8的倍数就用对齐字节补齐。4.2 内置锁Monitor的本质synchronized的锁本质是Java对象的监视器Monitor每个Java对象在创建时都会伴随一个Monitor的创建与对象生命周期一致。Monitor的结构可以简单理解为Monitor { Owner: null/线程ID // 持有锁的线程初始为null EntryList: 线程队列 // 等待获取锁的线程阻塞状态 WaitSet: 线程队列 // 调用wait()后阻塞的线程 RecursionCount: 0 // 重入计数器记录线程重入锁的次数 }锁的获取线程进入同步代码前会尝试获取Monitor的Owner权限。如果Owner为null当前线程成为OwnerRecursionCount1如果Owner是当前线程RecursionCount1重入锁特性如果Owner是其他线程当前线程进入EntryList变为BLOCKED状态。锁的释放线程执行完同步代码或调用wait()后RecursionCount-1。当RecursionCount为0时Owner变为null同时唤醒EntryList中的一个线程让它尝试获取锁。五、锁升级从偏向锁到重量级锁的进化在JDK1.6之前synchronized的实现非常简单粗暴——无论是否有线程竞争都直接使用重量级锁依赖操作系统的互斥量Mutex。但重量级锁有个致命问题线程切换时需要从用户态切换到内核态这个过程开销很大导致并发效率极低。为了解决这个问题JDK1.6引入了锁升级机制锁会根据线程竞争的激烈程度从无锁→偏向锁→轻量级锁→重量级锁逐步升级避免一开始就使用重量级锁带来的性能损耗。5.1 锁的四种状态锁的状态存储在对象的Mark Word中通过lock锁标志位和biased_lock偏向锁标志位区分四种状态的对比如下锁状态biased_lock偏向标志lock锁标志位Mark Word主要存储内容适用场景无锁001哈希码、GC分代年龄无线程竞争偏向锁101偏向线程ID、GC分代年龄单线程重复获取锁轻量级锁000锁记录指针指向线程栈帧多线程轻度竞争交替获取锁重量级锁010Monitor指针指向监视器多线程重度竞争同时争抢锁5.2 锁升级的详细流程5.2.1 1. 无锁状态场景Java对象刚创建时还没有任何线程尝试获取它的锁。Mark Word结构存储对象的哈希码调用hashCode()后会计算并存储、GC分代年龄锁标志位01偏向标志0。5.2.2 2. 偏向锁Biased Lock核心目标单线程重复获取锁时避免频繁的CAS操作提高效率。升级流程当第一个线程尝试获取锁时JVM检查Mark Word的偏向标志为0、锁标志位为01无锁状态。线程通过CAS操作将自己的线程ID写入Mark Word同时将偏向标志设为1变为偏向锁状态。后续该线程再次进入同步代码时只需检查Mark Word中的线程ID是否为自己无需再次CAS直接获取锁快速重入。偏向锁的撤销当有其他线程尝试获取该锁时偏向锁会被撤销升级为轻量级锁或重量级锁。撤销的触发条件多个线程竞争偏向锁第二个线程CAS修改Mark Word失败调用hashCode()方法Mark Word需要存储哈希码与偏向线程ID冲突。撤销流程会触发STWStop The World所有用户线程暂停JVM等待全局安全点所有用户线程停止执行遍历所有线程的栈帧检查是否有线程持有该锁的锁记录清空锁记录将Mark Word恢复为无锁状态清除偏向线程ID将锁升级为轻量级锁少数场景直接升级为重量级锁唤醒被阻塞的线程让它们竞争轻量级锁。注意由于偏向锁的撤销会触发STW对于高并发场景多线程频繁竞争锁偏向锁反而会降低性能。因此可以通过JVM参数-XX:-UseBiasedLocking关闭偏向锁默认开启。5.2.3 3. 轻量级锁Lightweight Lock核心目标多线程轻度竞争时通过CAS自旋避免升级为重量级锁自旋是用户态操作开销远小于内核态切换。升级流程偏向锁撤销后JVM会在抢锁线程的栈帧中创建一个锁记录Lock Record存储当前对象Mark Word的拷贝称为Displaced Mark Word。抢锁线程通过CAS操作尝试将对象Mark Word中的内容替换为锁记录指针指向自己栈帧中的Lock Record。如果CAS成功当前线程获取轻量级锁执行同步代码如果CAS失败说明有其他线程也在争抢锁竞争加剧轻量级锁会膨胀为重量级锁。5.2.4 4. 重量级锁Heavyweight Lock核心目标多线程重度竞争时通过操作系统的互斥量保证线程安全牺牲效率换稳定性。升级流程轻量级锁CAS自旋失败后JVM会将锁升级为重量级锁此时对象Mark Word中的内容替换为Monitor指针指向该对象对应的Monitor。后续争抢锁的线程会直接进入Monitor的EntryList变为BLOCKED状态释放CPU资源。持有锁的线程释放锁后会唤醒EntryList中的一个线程让它尝试获取Monitor的Owner权限。5.3 锁升级的核心总结锁升级是不可逆的一旦升级为重量级锁就不会再降级为轻量级锁或偏向锁。核心优化思路按需分配锁的开销——无竞争时用无锁单线程竞争用偏向锁低开销轻度竞争用轻量级锁自旋CAS重度竞争用重量级锁阻塞等待。实际开发启示高并发场景下应尽量避免锁竞争如使用局部变量、无锁编程如果必须使用锁也要控制锁的粒度如用同步块而非同步方法避免锁升级为重量级锁。六、Synchronized的完整执行流程结合前面的锁升级和Monitor原理我们可以梳理出synchronized的完整执行流程线程进入同步代码前首先检查锁对象的Mark Word如果biased_lock1且lock01可偏向状态检查Mark Word中的线程ID是否为当前线程是直接进入同步代码偏向锁重入否通过CAS尝试将线程ID写入Mark WordCAS成功则进入同步代码CAS失败则撤销偏向锁升级为轻量级锁。如果biased_lock0且lock00轻量级锁状态线程在栈帧中创建Lock Record存储Mark Word拷贝通过CAS尝试将Mark Word替换为Lock Record指针CAS成功则获取锁失败则自旋重试自旋超过阈值默认10次或有更多线程竞争轻量级锁膨胀为重量级锁。如果lock10重量级锁状态线程进入Monitor的EntryList变为BLOCKED状态等待被唤醒。线程执行同步代码如果线程再次进入同步代码重入轻量级锁会增加Lock Record计数重量级锁会增加RecursionCount。线程释放锁轻量级锁删除Lock Record通过CAS将Mark Word恢复为原始值重量级锁RecursionCount减1当计数为0时释放Monitor的Owner权限唤醒EntryList中的线程释放锁后线程退出同步代码。七、Synchronized的特性与线程间通信7.1 Synchronized的三大核心特性原子性临界区代码段的执行是不可中断的同一时间只有一个线程能执行由Monitor保证。可见性线程释放锁时会将工作内存中的修改同步到主内存线程获取锁时会从主内存重新读取最新值避免脏读。有序性禁止指令重排序JVM会在同步代码前后添加内存屏障保证代码执行顺序与编写顺序一致。7.2 线程间通信的三种方式多线程操作共享资源时需要通过通信协调执行顺序常见方式有等待-通知机制wait/notify基于synchronized实现线程通过wait()释放锁并阻塞通过notify()/notifyAll()唤醒阻塞线程如生产者-消费者模型。共享内存线程通过读写共享变量进行通信如用volatile修饰共享变量保证可见性。管道流通过PipedInputStream和PipedOutputStream实现线程间的字节流通信适用于线程间数据传输。其中等待-通知机制是synchronized的配套通信方式也是最常用的并发协调手段。八、总结与面试重点synchronized作为Java并发编程的基础是面试中的高频考点。核心要点总结如下核心知识点线程安全根源竞态条件导致指令交错synchronized通过锁定临界区保证原子性。使用方式同步块锁对象自定义粒度细、同步方法普通锁this静态锁Class。底层原理依赖Java对象的Monitor内置锁锁状态存储在Mark Word中。锁升级无锁→偏向锁→轻量级锁→重量级锁按需优化性能。核心特性原子性、可见性、有序性支持重入锁。面试常问问题synchronized和volatile的区别volatile仅保证可见性和有序性不保证原子性synchronized保证原子性、可见性、有序性。volatile用于变量级别的同步synchronized用于代码块/方法级别的同步。volatile不会阻塞线程synchronized会导致线程阻塞。synchronized是公平锁还是非公平锁非公平锁。唤醒线程时会随机选择EntryList中的一个线程不遵循先到先得公平锁会增加开销。synchronized的锁重入原理是什么轻量级锁通过栈帧中的Lock Record计数实现重量级锁通过Monitor的RecursionCount计数实现线程再次获取锁时只需增加计数无需重新竞争。为什么偏向锁在高并发场景下建议关闭高并发时偏向锁会频繁被撤销触发STWStop The World反而降低性能。通过-XX:-UseBiasedLocking关闭后直接从无锁升级为轻量级锁。生产者-消费者模型用synchronized怎么实现缓冲区作为临界区用synchronized锁定生产者用while(buffer满) wait()消费者用while(buffer空) wait()生产/消费后调用notifyAll()唤醒线程。通过本文的学习相信大家已经对synchronized的使用、原理、优化有了全面的理解。在实际开发中要根据并发场景选择合适的同步方式同时结合锁升级机制优化性能在面试中要能清晰阐述底层原理和实战经验展现自己的技术深度。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

建设企业网站企业网上银行登录网站开发学哪种语言

Zotero文献管理插件标签显示异常:从发现问题到完美解决的完整指南 【免费下载链接】zotero-style zotero-style - 一个 Zotero 插件,提供了一系列功能来增强 Zotero 的用户体验,如阅读进度可视化和标签管理,适合研究人员和学者。 …

张小明 2026/1/8 17:06:27 网站建设

卖汽车怎么做网站北京蓝杉网站建设公司

有需要的同学,源代码和配套文档领取,加文章最下方的名片哦 一、项目演示 项目演示视频 二、资料介绍 完整源代码(前后端源代码SQL脚本)配套文档(LWPPT开题报告)远程调试控屏包运行 三、技术介绍 Java…

张小明 2026/1/9 2:37:20 网站建设

做网站教程免费推广有哪些方式

还在为GTA5游戏体验不够丰富而困扰吗?想要解锁更多隐藏功能却无从下手?YimMenu作为专业的GTA5游戏增强工具,能够为你开启全新的游戏世界。本指南将详细介绍如何快速上手这款强大的修改工具,让你轻松掌握GTA5游戏增强技巧&#xff…

张小明 2026/1/9 2:37:17 网站建设

怎么做优惠券的网站数字广东网络建设有限公司地址

还在为昂贵的GPU和庞大的存储需求而烦恼吗?🚀 本文将带你解锁在普通CPU环境下运行AlphaFold的完整方案,让你用最低的成本体验前沿的蛋白质结构预测技术! 【免费下载链接】alphafold 项目地址: https://gitcode.com/gh_mirrors/…

张小明 2026/1/9 2:36:56 网站建设