偏向锁案例

wen java案例 1

本文目录导读:

偏向锁案例

  1. 偏向锁概述
  2. 实战案例
  3. 运行配置
  4. 性能测试对比
  5. 关键要点总结
  6. 常见问题

我将为您详细介绍偏向锁的概念、原理和实战案例。

偏向锁概述

什么是偏向锁?

偏向锁是Java synchronized关键字的一种优化机制,核心思想是:当一个线程第一次获取锁时,会在对象头和栈帧的锁记录中记录该线程ID,后续该线程再次进入同步块时,无需再进行CAS操作,直接进入。

适用场景

  • 只有一个线程访问同步块
  • 线程交替执行但竞争不激烈

实战案例

案例1:基础偏向锁演示

public class BiasedLockBasic {
    // 创建对象作为锁
    private static final Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        // JVM默认启动4秒后开启偏向锁,这里手动延迟
        Thread.sleep(5000);
        Runnable task = () -> {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() 
                    + " 获取偏向锁: " + LockHelper.getLockState(lock));
            }
        };
        // 同一个线程多次获取锁
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                synchronized (lock) {
                    System.out.println("第" + (i+1) + "次获取:" 
                        + LockHelper.getLockState(lock));
                }
            }
        }, "单线程");
        thread.start();
        Thread.sleep(1000);
        // 锁状态查看
        System.out.println("最终锁状态: " + LockHelper.getLockState(lock));
    }
}
// 锁状态查看工具类
class LockHelper {
    public static String getLockState(Object obj) {
        // 使用JOL工具获取对象头信息
        return org.openjdk.jol.info.ClassLayout.parseInstance(obj)
            .toPrintable();
    }
}

案例2:偏向锁升级演示

public class BiasedLockUpgrade {
    private static final Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000); // 等待偏向锁启动
        // 阶段1:单独一个线程获取锁(偏向锁)
        System.out.println("=== 阶段1:单线程获取锁 ===");
        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程1获取锁");
                System.out.println("锁对象信息: " + 
                    org.openjdk.jol.info.ClassLayout.parseInstance(lock).toPrintable());
            }
            // 线程1结束,但偏向锁仍然存在
            System.out.println("线程1执行完毕,锁状态: ");
            System.out.println(org.openjdk.jol.info.ClassLayout.parseInstance(lock).toPrintable());
        }, "线程1");
        thread1.start();
        thread1.join();
        Thread.sleep(1000);
        // 阶段2:另一个线程尝试获取锁(锁升级为轻量级锁)
        System.out.println("\n=== 阶段2:第二个线程获取锁 ===");
        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程2获取锁");
                System.out.println("锁对象信息: " + 
                    org.openjdk.jol.info.ClassLayout.parseInstance(lock).toPrintable());
            }
        }, "线程2");
        thread2.start();
        thread2.join();
        // 阶段3:多个线程竞争(升级为重量级锁)
        System.out.println("\n=== 阶段3:多线程竞争 ===");
        // 制造竞争
        Thread[] threads = new Thread[3];
        CountDownLatch latch = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            threads[i] = new Thread(() -> {
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName() + " 获取锁");
                    try {
                        Thread.sleep(100); // 保持持有锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                latch.countDown();
            }, "竞争线程" + i);
            threads[i].start();
        }
        latch.await();
        System.out.println("最终锁状态: ");
        System.out.println(org.openjdk.jol.info.ClassLayout.parseInstance(lock).toPrintable());
    }
}

案例3:偏向锁批量撤销

public class BiasedLockBatchRevocation {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        // 创建多个锁对象
        List<Object> locks = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            locks.add(new Object());
        }
        // 第一个线程获取所有锁(偏向锁)
        System.out.println("=== 第一个线程获取所有锁 ===");
        Thread thread1 = new Thread(() -> {
            for (Object lock : locks) {
                synchronized (lock) {
                    // 不做任何操作
                }
            }
        }, "偏向线程");
        thread1.start();
        thread1.join();
        // 第二个线程获取所有锁(触发批量撤销)
        System.out.println("=== 第二个线程获取所有锁 ===");
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < locks.size(); i++) {
                Object lock = locks.get(i);
                synchronized (lock) {
                    System.out.println("锁 " + i + " 状态: " + 
                        org.openjdk.jol.info.ClassLayout.parseInstance(lock).toPrintable().split("\n")[2]);
                }
            }
        }, "撤销线程");
        thread2.start();
        thread2.join();
        // 查看批量撤销后新对象的状态
        System.out.println("=== 批量撤销后新创建的对象 ===");
        Object newLock = new Object();
        System.out.println("新对象锁状态: " + 
            org.openjdk.jol.info.ClassLayout.parseInstance(newLock).toPrintable());
    }
}

案例4:哈希码与偏向锁

public class BiasedLockHashCode {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        Object lock = new Object();
        System.out.println("初始状态(无偏向锁): ");
        System.out.println(org.openjdk.jol.info.ClassLayout.parseInstance(lock).toPrintable());
        // 获取哈希码会撤销偏向锁
        System.out.println("调用hashCode后: ");
        int hash = lock.hashCode();
        System.out.println("哈希值: " + hash);
        System.out.println(org.openjdk.jol.info.ClassLayout.parseInstance(lock).toPrintable());
        // 偏向锁与hashCode不能共存
        Object lock2 = new Object();
        Thread thread = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("获取偏向锁: ");
                System.out.println(org.openjdk.jol.info.ClassLayout.parseInstance(lock2).toPrintable());
                // 在持有偏向锁时调用hashCode
                System.out.println("偏向锁时调用hashCode: ");
                System.out.println("hash值: " + lock2.hashCode());
                System.out.println(org.openjdk.jol.info.ClassLayout.parseInstance(lock2).toPrintable());
            }
        });
        thread.start();
        thread.join();
    }
}

运行配置

需要添加JVM参数和依赖:

<!-- Maven依赖 -->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.16</version>
</dependency>
# JVM运行参数
# 开启偏向锁(JDK8默认开启,但延迟4秒)
-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
# 或者完全关闭偏向锁
-XX:-UseBiasedLocking
# 设置批量撤销阈值
-XX:BiasedLockingBulkRevokeThreshold=20
-XX:BiasedLockingBulkRebiasThreshold=40

性能测试对比

public class BiasedLockPerformance {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        int threadCount = 1; // 单线程场景
        int iterations = 1_000_000;
        // 测试偏向锁
        long start = System.currentTimeMillis();
        runSingleThread(iterations);
        long biasedLockTime = System.currentTimeMillis() - start;
        System.out.println("偏向锁耗时: " + biasedLockTime + "ms");
    }
    private static void runSingleThread(int iterations) {
        Object lock = new Object();
        for (int i = 0; i < iterations; i++) {
            synchronized (lock) {
                // 模拟业务操作
                int x = i + 1;
            }
        }
    }
}

关键要点总结

偏向锁的优势

  1. 减少CAS操作:同一线程重复获取锁时无需CAS
  2. 提高单线程性能:和未加锁性能基本一致
  3. 适合读多写少:多个线程读取但只有一个写入的场景

偏向锁的代价

  1. 批量撤销开销:当锁竞争激烈时会频繁撤销
  2. 暂停所有线程:批量撤销时需要STW
  3. 内存占用:每个对象头都需要存储偏向线程ID

最佳实践建议

  • JDK8:默认开启偏向锁,但有4秒延迟
  • JDK15:默认禁用偏向锁
  • 高竞争场景:考虑使用 -XX:-UseBiasedLocking 关闭
  • 单线程场景:可以设置偏向锁延迟为0

常见问题

// 问题1:偏向锁被另一个线程获取
public class BiasedLockQuestion {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        Object lock = new Object();
        // 线程A获取偏向锁
        Thread a = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程A持有锁");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {}
            }
        }, "A");
        a.start();
        a.join();
        // 线程B尝试获取(触发撤销)
        Thread b = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程B获取锁(偏向锁撤销)");
            }
        }, "B");
        b.start();
        b.join();
        // 线程C再次获取(锁已升级为轻量级)
        Thread c = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程C获取锁(已升级)");
            }
        }, "C");
        c.start();
        c.join();
    }
}

希望这些案例能帮助您深入理解偏向锁的工作机制和实际应用场景!

抱歉,评论功能暂时关闭!