php网站建设安装环境,为wordpress开发app,为什么要做外贸网站,网站模板论坛并发关键字
synchronized关键字
在应用Sychronized关键字时需要把握如下注意点#xff1a;
1.一把锁只能同时被一个线程获取#xff0c;没有获得锁的线程只能等待#xff1b;
2.每个实例都对应有自己的一把锁(this),不同实例之间互不影响#xff1b;例外#xff1a;锁…并发关键字
synchronized关键字
在应用Sychronized关键字时需要把握如下注意点
1.一把锁只能同时被一个线程获取没有获得锁的线程只能等待
2.每个实例都对应有自己的一把锁(this),不同实例之间互不影响例外锁对象是*.class以及synchronized修饰的是static方法的时候所有对象公用同一把锁
3.synchronized修饰的方法无论方法正常执行完毕还是抛出异常都会释放锁
synchronized关键字的基本用法 修饰一个代码块被修饰的代码块称为同步语句块其作用的范围是大括号{}括起来的代码作用的对象是调用这个代码块的对象 ---对象锁 synchronized (new Object()) {System.out.println(block1锁,我是线程 Thread.currentThread().getName());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(block1锁,Thread.currentThread().getName() 结束);}
synchronized (new Object()) {System.out.println(block2锁,我是线程 Thread.currentThread().getName());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(block2锁,Thread.currentThread().getName() 结束);} 这种情况两个代码块获取的是两把锁 修饰一个方法被修饰的方法称为同步方法其作用的范围是整个方法作用的对象是调用这个方法的对象 --对象锁 修饰一个静态的方法其作用的范围是整个静态方法作用的对象是这个类的所有对象 ---类锁 修饰一个类其作用的范围是synchronized后面括号括起来的部分作用主的对象是这个类的所有对象。--类锁 修饰一个代码块synchronized()括号中如果是class对象则获取的是类锁。
synchronized关键字的实现原理
synchronized 是 Java 中实现线程同步的核心机制其底层实现基于 对象监视器Monitor、对象头中的锁状态标记 和 锁升级优化。以下从 JVM 层、操作系统层和硬件层逐步解析其实现原理。
1.对象监视器
Monitor 是 JVM 实现同步的核心机制每个对象关联一个 Monitor。其结构包括 Owner当前持有锁的线程。 EntryList等待锁的线程队列处于 BLOCKED 状态。 WaitSet调用 wait() 后进入等待的线程队列处于 WAITING 或 TIMED_WAITING 状态。
Monitor 的工作流程 线程尝试通过 CAS 修改对象头获取锁 若成功设置 Owner 为当前线程进入临界区。 若失败线程进入 EntryList 阻塞等待。 释放锁时Owner 清空唤醒 EntryList 中的线程重新竞争。 从图中可以看出每个线程对Object对象的访问首先要获得Object的监视器如果获取失败该线程就进入同步状态线程状态变为BLOCKED当Object的监视器占有者释放后在同步队列中得线程就会有机会重新获取该监视器。
2.对象头与状态标记
每个Java对象在内存中分为3部分 对象头Header存储锁状态、GC 信息、哈希码等。 实例数据Instance Data对象的成员变量。 对齐填充Padding确保对象按 8 字节对齐。
对象头结构 Mark Word64 bits存储锁状态、线程 ID、GC 分代年龄等。 Klass Pointer64 bits指向类元数据的指针。 3.锁升级优化
synchronized实现的同步锁1.6之前称之为重量级锁重量锁会直接使用操作系统的底层的锁会造成线程排队串行执行且会使CPU在用户态和核心态之间频繁切换所以代价高、效率低。为了提高效率从Java 1.6开始JVM进行了优化synchronized不一定直接使用重量锁一共有四种状态无锁、偏向锁、轻量级锁和重量级锁。 锁膨胀方向无锁——偏向锁——轻量级锁——重量级锁 锁只可以升级不可降级 偏向锁
核心思想偏向于第一个获取锁的线程锁对象会记住首次访问它的线程 ID记录在对象头的 Mark Word 中。若后续没有其他线程竞争该线程再次进入同步块时 无需加锁/解锁操作仅需检查线程 ID 是否匹配。
1. 初次获取锁 CAS 设置线程 ID通过 CAS 操作将当前线程 ID 写入对象的 Mark Word。 标记为偏向模式对象头中的锁标志位更新为偏向锁状态101。
2. 再次进入同步块 检查线程 ID判断对象头中的线程 ID 是否与当前线程一致 ✅ 一致直接执行代码无任何同步开销。 ❌ 不一致检查对象是否仍可偏向 可偏向尝试通过 CAS 竞争锁重新偏向。 已偏向其他线程触发 偏向锁撤销可能升级为轻量级锁。
偏向锁是针对于单个线程而言的线程获得锁之后就不会再有解锁等操作了这样可以省略很多开销。假如有两个线程来竞争该锁话那么偏向锁就失效了进而升级成轻量级锁了
轻量锁 JVM 在存在低强度线程竞争时采用的锁优化机制其核心是通过 CAS 自旋 减少线程阻塞的开销避免直接升级为重量级锁操作系统互斥锁。
核心思想通过线程的 CAS 自旋循环尝试获取锁替代直接阻塞线程降低上下文切换的开销。适用于 线程交替执行同步块、竞争短暂且稀疏 的场景。
1. 加锁过程 创建 Lock Record 线程进入同步块时在栈帧中分配一个 Lock Record用于保存锁对象的原始 Mark Word。 CAS 竞争锁 尝试通过 CAS 操作将对象头的 Mark Word 更新为指向 Lock Record 的指针 ✅ 成功对象头的锁标志位变为 00轻量级锁状态线程获得锁。 ❌ 失败说明存在竞争触发 自旋重试 或 锁升级。
2. 解锁过程 CAS 还原 Mark Word 通过 CAS 将 Lock Record 中保存的原始 Mark Word 写回对象头。 还原成功 对象恢复为无锁状态标志位 01。 还原失败 说明锁已升级为重量级锁需通过操作系统级别的锁机制释放。
轻量级锁的升级与自旋优化 所谓自旋就是指当有另外一个线程来竞争锁时这个线程会在原地循环等待而不是把该线程给阻塞直到那个获得锁的线程释放锁之后这个线程就可以马上获得锁的。注意锁在原地循环的时候是会消耗cpu的就相当于在执行一个啥也没有的for循环。所以轻量级锁适用于那些同步代码块执行的很快的场景这样线程原地等待很短很短的时间就能够获得锁了。但是也有问题1.如果同步代码块执行的很慢需要消耗大量的时间那么这个时侯其他线程在原地等待空消耗cpu。2.本来一个线程把锁释放之后当前线程是能够获得锁的但是假如这个时候有好几个线程都在竞争这个锁的话那么有可能当前线程会获取不到锁还得原地等待继续空循环消耗cup甚至有可能一直获取不到锁 1. 自旋策略 固定次数自旋早期 JVM 采用固定次数的自旋如 10 次若失败则升级为重量级锁。 自适应自旋Adaptive SpinningJDK 1.6 后引入根据 历史自旋成功率动态调整自旋次数如上次成功则增加次数失败则减少。
2. 升级条件 自旋失败多次 CAS 尝试后仍无法获取锁。 竞争加剧超过 JVM 自旋阈值由 -XX:PreBlockSpin 控制默认值因 JVM 实现而异。 升级路径轻量级锁 → 重量级锁线程阻塞依赖操作系统互斥量。
重量锁是 JVM 在锁竞争激烈时的最终锁机制其核心是 依赖操作系统互斥量Mutex和条件变量Condition Variables实现线程同步通过线程阻塞和唤醒机制解决高并发竞争问题。当轻量级锁自旋失败线程竞争激烈时升级为重量级锁通过 操作系统内核调度 管理线程阻塞与唤醒。操作系统级别的锁机制通常支持公平性策略如 FIFO 队列避免线程饥饿。
synchronized关键字的特性 可重入性又称递归锁同一个线程在外层方法获取锁的时候再进入该线程的内层方法会自动获取锁。前提锁对象是同一个对象或class不会因为之前已经获取就阻塞。 public class ReentrantDemo {public synchronized void methodA() {methodB(); // 可重入直接进入 methodB 的同步块}public synchronized void methodB() {// 代码逻辑}
} 内存可见性线程释放锁时会将共享变量的修改刷新到主内存获取锁时会从主内存重新加载变量值 有序性禁止指令重排序临界区内的代码不会被编译器或处理器重排序破坏逻辑。
synchronized关键字的优化策略
1. 锁消除Lock Elimination 触发条件JIT 编译器检测到不存在共享数据竞争的锁。 示例局部对象锁线程私有无需同步。 public void lockEliminationDemo() {Object localLock new Object();synchronized (localLock) { // 锁被消除System.out.println(This lock is unnecessary);}
}
2. 锁粗化Lock Coarsening 触发条件多次连续的锁操作合并为一次减少锁开销。 示例循环内重复加锁。 public void lockCoarseningDemo() {synchronized (this) {// 合并多次锁操作为一次for (int i 0; i 100; i) {// 操作共享资源}}
}
3. 自旋优化Adaptive Spinning 轻量级锁失败后线程不立即阻塞而是自旋重试默认次数为 10 次JDK 6 后改为自适应。
volatile关键字
volatile 是 Java 中用于解决 多线程内存可见性 和 指令重排序 问题的关键字。它提供了一种轻量级的同步机制确保变量的修改对所有线程立即可见同时禁止编译器和处理器对代码进行某些优化。注意不保证复合操作的原子性需结合锁或原子类使用。
volatile的核心作用 保证可见性在多线程环境下每个线程可能将共享变量缓存到自己的 工作内存CPU 缓存 中导致一个线程修改了变量的值其他线程无法立即看到最新值若使用volatile修饰变量每次读写都直接操作 主内存绕过线程的工作内存。强制其他线程在读取 volatile 变量时清空本地缓存重新从主内存加载最新值。 public class VisibilityDemo {private volatile boolean flag false;
public void writer() {flag true; // 写操作立即刷新到主内存}
public void reader() {while (!flag) { // 每次读取都从主内存加载最新值// 循环等待}System.out.println(Flag is now true);}
} 禁止指令重排序 问题背景 编译器和处理器为了提高性能可能会对代码执行顺序进行 重排序如单例模式中的双重检查锁定问题。 volatile 的解决方案 通过插入 内存屏障Memory Barrier禁止对 volatile 变量前后的指令进行重排序。 确保 volatile 变量的写操作对其他线程可见的顺序符合程序预期。 public class Singleton {private static volatile Singleton instance;
public static Singleton getInstance() {if (instance null) {synchronized (Singleton.class) {if (instance null) {instance new Singleton(); // 禁止重排序确保对象完全初始化}}}return instance;}
}
volatile的实现原理
1. 内存屏障Memory Barrier又称内存栅栏是一个cpu指令。
JVM 会在 volatile 变量的读写操作前后插入特定类型的内存屏障确保以下两点 可见性强制将工作内存的修改刷新到主内存或从主内存重新加载变量值。 有序性禁止编译器或处理器对指令进行重排序。
为了提高处理速度处理器不直接和内存进行通信而是先将系统内存的数据读到内部缓存(L1、L2或其他)后再进行操作但操作完不知道何时会写到内存。如果对声明了volatile的变量进行写操作JVM就会向处理器发送一条lock前缀的指令将这个变量所在缓存行的数据写到系统内存。为了保证各个处理器的缓冲是一致的实现缓存一致性协议每个处理器通过嗅探在总线上传播的数据来检查自己缓冲的值是不是过期了当处理器发现自己缓存行对应的 内存的地址被修改了就会将当前处理器的缓存行设置为无效状态当处理器对这个数据进行修改操作的时候会重新从系统内存中把数据读到处理器缓冲中。所有多核处理器发现本地缓存失效后就会从内存中重读该变量的数据也就获取到了最新的值。
屏障类型作用LoadLoad确保当前读操作之前的其他读操作已完成。StoreStore确保当前写操作之前的其他写操作对其他线程可见。LoadStore确保当前读操作之后的写操作不会被重排序到读操作之前。StoreLoad确保当前写操作之后的所有读/写操作不会被重排序到写操作之前全能屏障开销最大。
2. 具体规则 写操作Write 在写 volatile 变量后插入 StoreStore 和 StoreLoad 屏障确保 当前变量的修改对其他线程可见。 写操作不会被重排序到后续操作之后。 读操作Read 在读 volatile 变量前插入 LoadLoad 和 LoadStore 屏障确保 后续操作不会被重排序到读操作之前。 每次读取都能获取最新值。
volatile的使用场景
1.状态标志位
多线程中通过 volatile 变量作为开关控制线程执行。
public class TaskRunner implements Runnable {private volatile boolean running true;
public void stop() {running false; // 其他线程调用此方法后立即停止任务}
Overridepublic void run() {while (running) {// 执行任务}}
}
2.单例模式
通过 volatile 解决双重检查锁定中的重排序问题
3.无锁编程
volatile 与 synchronized 的对比
维度volatilesynchronized可见性保证变量的可见性保证临界区内所有变量的可见性原子性仅单次读/写操作原子保证代码块内操作的原子性有序性禁止指令重排序通过锁机制隐式保证有序性性能轻量级无上下文切换开销重量级涉及锁升级和线程阻塞适用场景状态标志、单次发布、无锁编程复杂同步逻辑、复合操作
final关键字
final的作用
修饰类禁止类被继承即不可有子类。
修饰方法禁止方法被子类重写Override。但是可以重载。
修饰变量 基本类型变量变量值不可修改必须在声明时或构造方法中初始化。 final int MAX_VALUE 100; // 声明时初始化
final double PI;
public MyClass() { PI 3.14; } // 构造方法中初始化 引用类型变量引用指向的对象不可变但对象内部状态可能可变。 final ListString list new ArrayList();
list.add(Java); // 允许操作对象内容
// list new LinkedList(); // 编译错误禁止重新赋值 常量定义全局常量 public static final String LOG_TAG System;