Java synchronized案例怎么用

wen java案例 28

Java synchronized案例怎么用:从入门到实战的完整指南

目录导读

  1. synchronized核心概念速览
  2. 基础用法案例:同步方法与同步块
  3. 经典实战:多线程卖票系统
  4. 进阶陷阱:错误使用案例与修正
  5. 性能对比:synchronized vs Lock
  6. 常见问答FAQ
  7. 总结与最佳实践

synchronized核心概念速览

Q: synchronized到底锁的是什么?
A: 锁的是对象实例Class对象

Java synchronized案例怎么用

  • 修饰实例方法:锁当前实例this
  • 修饰静态方法:锁当前类的Class对象
  • 修饰代码块:锁括号内指定的对象

关键原则:多个线程必须竞争同一把锁才能实现互斥,如果锁不同,synchronized将失效。


基础用法案例:同步方法与同步块

案例1:同步实例方法

public class Counter {
    private int count = 0;
    public synchronized void increment() { count++; }
    // 等价于 synchronized(this) { count++; }
}

适用场景:整个方法都需要同步,且锁对象明确。

案例2:同步静态方法

public class StaticCounter {
    private static int count = 0;
    public static synchronized void increment() { count++; }
    // 锁Class对象,所有实例共享同一把锁
}

案例3:同步代码块(精准锁定)

public class FineGrainedLock {
    private final Object lock = new Object();
    public void method() {
        // 非同步代码...
        synchronized (lock) {
            // 仅同步临界区代码
        }
    }
}

优势:缩小同步范围,提升并发性能。


经典实战:多线程卖票系统

问题描述

模拟3个窗口同时卖100张票,要求:

  • 不能超卖(同一张票不能卖两次)
  • 最终剩余票数为0

错误实现(线程不安全)

class Ticket {
    private int tickets = 100;
    public void sell() {
        if (tickets > 0) {
            try { Thread.sleep(10); } catch (InterruptedException e) {}
            tickets--;
        }
    }
}
// 3个线程各卖100次,结果剩余票数可能为负数

正确实现(synchronized锁实例)

class SafeTicket {
    private int tickets = 100;
    public synchronized void sell() {  // 锁this
        if (tickets > 0) {
            try { Thread.sleep(10); } catch (InterruptedException e) {}
            tickets--;
        }
    }
}
public class Test {
    public static void main(String[] args) throws InterruptedException {
        SafeTicket ticket = new SafeTicket();
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                while (ticket.available() > 0) {
                    ticket.sell();
                }
            }).start();
        }
    }
}

运行结果:剩余票数始终为0,无负数。

注意事项

  • 锁必须一致:若使用SafeTicket的不同实例,锁失效
  • 避免在锁内执行耗时I/O:会阻塞其他线程

进阶陷阱:错误使用案例与修正

陷阱1:锁字符串常量

// 错误:字符串常量池导致不同对象共享同一锁
synchronized ("LOCK") { ... }
// 修正:使用new Object()或指定唯一对象
private final Object lock = new Object();
synchronized (lock) { ... }

陷阱2:锁Integer等包装类

// 错误:Integer缓存池(-128~127)导致锁范围扩大
Integer lock = 1;
synchronized (lock) { ... }
// 修正:使用最终常量或专用锁对象
private final Object lock = new Object();

陷阱3:synchronized与非同步混用

class Data {
    private int value;
    public synchronized void set(int v) { value = v; }
    public int get() { return value; } // 未同步!导致读取脏数据
}
// 修正:读取方法也要同步,或使用volatile+原子类

性能对比:synchronized vs Lock

特性 synchronized Lock (ReentrantLock)
锁获取方式 隐式 显式lock()/unlock()
可中断性 不支持 支持lockInterruptibly()
公平锁 非公平 可选公平/非公平
性能 低竞争下优于Lock 高竞争下更稳定
语法简洁 自动释放锁 必须finally解锁

何时用synchronized

  • 同步块简单,无需高级特性
  • 代码可读性优先
  • 低并发场景

何时用Lock

  • 需要超时、可中断、尝试获取锁
  • 需要公平锁
  • 高并发写场景

常见问答FAQ

Q1:synchronized能锁住int吗?

A:不能,synchronized只能锁对象,int是原始类型,需用Integer包装类,但注意缓存陷阱。

Q2:静态方法用synchronized锁类,会影响实例方法吗?

A:不会,实例方法锁this,静态方法锁Class,两者互不干扰。

Q3:synchronized遇到异常会释放锁吗?

A:会,JVM自动释放锁,即使发生异常或return。

Q4:多个synchronized方法嵌套是否会导致死锁?

A:可能,例如A线程锁obj1请求obj2,B线程锁obj2请求obj1,解决方案:全局排序锁顺序。

Q5:String作为锁对象是否安全?

A:不安全,字符串常量池导致不同线程可能持有相同锁对象,用new Object()确保唯一性。


总结与最佳实践

核心原则

  1. 锁对象唯一:避免使用字符串常量、Integer缓存
  2. 锁定范围最小:能用同步块不用方法,能用代码块不用全方法
  3. 锁内的操作尽量轻量:避免I/O、网络请求等耗时操作
  4. 使用专用锁对象:声明为private final Object,避免外部干扰

代码模板

public class ThreadSafeService {
    private final Object lock = new Object();
    private final Map<String, String> cache = new HashMap<>();
    public String getData(String key) {
        // 非同步预处理
        synchronized (lock) {
            // 1. 检查缓存
            if (cache.containsKey(key)) return cache.get(key);
            // 2. 计算数据(耗时操作)
            String result = computeExpensive(key);
            // 3. 放入缓存
            cache.put(key, result);
            return result;
        }
    }
}

避坑清单

  • [x] 锁是否可能为null
  • [x] 是否存在字符串常量池风险?
  • [x] 锁内的代码是否可能长时间阻塞?
  • [x] 是否所有写操作都加了同步?
  • [x] 读取操作是否需要同步保证可见性?

通过本文的案例和陷阱分析,你应该能从容应对90%的Java同步开发场景。简单问题用synchronized,复杂场景用Lock,选对工具事半功倍。

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