网站设计O2O平台优化wordpress 插件启用
2026/6/20 12:40:12 网站建设 项目流程
网站设计O2O平台优化,wordpress 插件启用,打开这个网站,iis7.5网站权限配置目录 1、竞态条件 1.1、问题本质 1.2、解决方案 2、死锁 2.1、死锁四要素 2.2、Java 死锁 2.3、如何检测死锁 2.4、预防策略 3、性能开销 3.1、锁竞争 3.2、可维护性 4、现代替代方案 前沿 编写正确的并发程序#xff0c;比登天还难。 当一个线程运行…目录1、竞态条件1.1、问题本质1.2、解决方案2、死锁2.1、死锁四要素2.2、Java 死锁2.3、如何检测死锁2.4、预防策略3、性能开销3.1、锁竞争3.2、可维护性4、现代替代方案前沿编写正确的并发程序比登天还难。当一个线程运行时候的整个生命周期如下所示在多核 CPU 成为主流的今天Java 多线程Multithreading被视为提升系统吞吐量和响应速度的“银弹”。然而并发不是功能而是复杂性的放大器。设计更复杂虽然有一些多线程应用程序比单线程的应用程序要简单但其他的一般都更复杂。在多线程访问共享数据的时候这部分代码需要特别的注意线程之间的交互往往非常复杂。如下所示上下文切换的开销当CPU从执行一个线程切换到执行另外一个线程的时候它需要先存储当前线程的本地的数据程序指针等然后载入另一个线程的本地数据程序指针等最后才开始执行。这种切换称为“上下文切换”(“context switch”)。CPU会在一个上下文中执行一个线程然后切换到另外一个上下文中执行另外一个线程。上下文切换并不廉价。如果没有必要应该减少上下文切换的发生。本文将结合真实代码、底层原理与前沿实践系统性地揭示 Java 多线程编程的六大核心缺点并探讨如何在享受并发红利的同时规避其陷阱。1、竞态条件1.1、问题本质当多个线程无序访问共享可变状态且至少有一个线程在修改数据时最终结果依赖于线程调度的时序导致不可预测的行为。Java 代码示例public class UnsafeCounter { private int count 0; public void increment() { count; // 非原子操作 } public int getCount() { return count; } } // 测试代码 public static void main(String[] args) throws InterruptedException { UnsafeCounter counter new UnsafeCounter(); Thread t1 new Thread(() - { for (int i 0; i 100_000; i) counter.increment(); }); Thread t2 new Thread(() - { for (int i 0; i 100_000; i) counter.increment(); }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(Expected: 200000, Actual: counter.getCount()); // 输出可能为138456, 199872, ... 永远不等于 200000 }为什么 count不安全JVM 字节码层面count 被分解为三步// 字节码简化 getfield count // 读取 iconst_1 // 加1 putfield count // 写回若两个线程同时执行到 getfield它们会读取相同的值导致更新丢失。1.2、解决方案使用 synchronizedpublic synchronized void increment() { count; }使用 AtomicInteger无锁 CASprivate AtomicInteger count new AtomicInteger(0); public void increment() { count.incrementAndGet(); }2、死锁2.1、死锁四要素如下所示1.互斥Mutual Exclusion线程对所分配到的资源进行排他性使用即在一段时间内某资源只由一个线程占用。如果此时还有其它线程请求该资源则请求者只能等待直至占有资源的线程用毕释放。2.持有并等待Hold and Wait线程已经保持了至少一个资源但又提出了新的资源请求而该资源已被其它线程占有此时请求线程阻塞但又对自己已获得的其它资源保持不放。3.不可抢占No Preemption线程已获得的资源在未使用完之前不能被剥夺只能在使用完时由自己释放。4.循环等待Circular Wait在发生死锁时必然存在一个线程 —— 资源的环形链即线程集合 {T0T1T2・・・Tn} 中的 T0 正在等待一个 T1 占用的资源T1 正在等待 T2 占用的资源……Tn 正在等待已被 T0 占用的资源。更多关于死锁的知识可参考有关Java死锁和活锁的联系2.2、Java 死锁示例public class DeadlockDemo { private final Object lockA new Object(); private final Object lockB new Object(); public void methodA() { synchronized (lockA) { System.out.println(Thread Thread.currentThread().getName() got lockA); try { Thread.sleep(100); } catch (InterruptedException e) {} synchronized (lockB) { // 等待 lockB System.out.println(Acquired both locks); } } } public void methodB() { synchronized (lockB) { System.out.println(Thread Thread.currentThread().getName() got lockB); try { Thread.sleep(100); } catch (InterruptedException e) {} synchronized (lockA) { // 等待 lockA System.out.println(Acquired both locks); } } } public static void main(String[] args) { DeadlockDemo demo new DeadlockDemo(); new Thread(demo::methodA, T1).start(); new Thread(demo::methodB, T2).start(); // 程序将永久挂起 } }2.3、如何检测死锁jstackjstack pid 会自动检测死锁并打印Found one Java-level deadlock: T2: waiting to lock 0x000000076b8a1234 (lockA) T1: waiting to lock 0x000000076b8a5678 (lockB)2.4、预防策略锁排序所有线程按固定顺序获取锁如先 A 后 B超时机制tryLock(timeout)避免嵌套锁3、性能开销要知道并发≠加速有时更慢。3.1、锁竞争高并发下线程频繁争抢同一把锁导致上下文切换开销用户态 ↔ 内核态CPU 缓存失效False SharingFalse Sharing 示例public class FalseSharing implements Runnable { public final static int NUM_THREADS 4; public final static long ITERATIONS 500L * 1000L * 1000L; private final int arrayIndex; private static VolatileLong[] longs new VolatileLong[NUM_THREADS]; static { for (int i 0; i longs.length; i) { longs[i] new VolatileLong(); } } public FalseSharing(final int arrayIndex) { this.arrayIndex arrayIndex; } public void run() { long i ITERATIONS 1; while (0 ! --i) { longs[arrayIndex].value i; // 多个线程写相邻内存 } } public final static class VolatileLong { public volatile long value 0L; // 在 Java 8 可加 Contended 注解避免 False Sharing } }若 VolatileLong 对象位于同一 CPU 缓存行64 字节一个核心修改会使其他核心缓存失效性能下降 10 倍以上。2. 内存消耗每个 Java 线程默认栈大小1MB64位 JVM1000 个线程 ≈ 1GB 内存仅用于栈3. 上下文切换成本切换一次约1~10 微秒线程数 CPU 核心数时吞吐量反而下降在 16 核机器上当线程数超过 32吞吐量开始下降。如下所示3.2、可维护性1. 逻辑碎片化业务逻辑被 synchronized、wait/notify、Lock 切割得支离破碎。2. 异常处理陷阱synchronized (lock) { try { // 业务逻辑 } finally { // 必须释放锁否则死锁 lock.unlock(); // ReentrantLock 需手动释放 } }3. 中断处理困难如何安全停止一个运行中的线程Thread.stop() 已废弃不安全正确做法使用协作式中断Thread.interrupt() 检查 isInterrupted()4、现代替代方案超越传统多线程面对多线程的诸多缺点Java 社区正转向更安全的并发模型Java 21 虚拟线程示例告别线程池try (var executor Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10_000).forEach(i - { executor.submit(() - { // 每个任务一个虚拟线程几乎无开销 return doWork(i); }); }); } // 自动 join 所有虚拟线程虚拟线程不解决竞态条件但极大降低线程管理成本。总结缺点根本原因应对策略竞态条件共享可变状态使用不可变对象、原子类、同步块死锁循环等待锁锁排序、超时、避免嵌套性能开销锁竞争、上下文切换分拆大事务、减少共享、用无锁结构调试困难非确定性使用 TSan、增加日志、单元测试覆盖可维护性差逻辑分散封装并发原语、使用高级工具类资源消耗大线程栈开销采用虚拟线程、协程、异步模型

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询