
synchronized 锁本身不区分“数据类型”,它锁的是对象(或类),而不是 int、String、List 这些类型本身。真正影响表现的,是被锁对象的**共享性**和**生命周期**——也就是多个线程是否操作同一份数据,以及它们用的是不是同一个锁对象。
锁的是对象,不是变量类型
无论你操作的是 int、String、Map 还是自定义对象,synchronized 起作用的前提只有一个:多个线程在竞争访问**同一个共享对象上的临界区**。比如:
- 一个 int count 成员变量,如果被多个线程通过同一个 Ticket 实例去修改,且方法加了 synchronized,那就安全;
- 但如果每个线程都 new 了一个新 Ticket(),哪怕都改的是 count,也互不影响——因为锁的是不同实例;
- String 是不可变的,但如果你用它做锁对象(synchronized(str)),要注意字符串常量池带来的意外共享(比如两个线程传入字面量 "lock",实际锁的是同一个 String 对象),反而可能扩大锁范围,引发不必要阻塞。
基本类型包装类要特别小心
Integer、Boolean 等包装类有缓存机制(如 Integer 缓存 -128~127),直接拿它们当锁对象容易踩坑:
- synchronized(new Integer(100)) —— 每次 new 都是新对象,锁无效;
- synchronized(Integer.valueOf(100)) —— 因为缓存,可能多个线程锁到同一个 Integer 实例,造成意料外的串行;
- 更稳妥的做法是用专用锁对象:private final Object lock = new Object();,避免任何隐式共享。
集合类的同步不能只靠 synchronized 方法
像 ArrayList、HashMap 这些非线程安全集合,即使你在自己的方法里加了 synchronized,也只是锁住了当前方法调用,无法保证复合操作原子性:
- 比如 if (!list.isEmpty()) list.remove(0); —— 判断和删除是两步,中间可能被其他线程插入;
- synchronized(this) 只能保护这一段代码块,但若 list 本身被多个类或方法共享,别的地方没加锁,照样出问题;
- 正确做法:要么用 Collections.synchronizedList() 包装,要么改用 CopyOnWriteArrayList 或 ConcurrentHashMap 等并发容器。
静态字段与类锁配合才有效
如果共享数据是 static 的(比如 static int totalCount),就必须用类锁来保护:
- synchronized(this) 或普通实例方法锁不住 static 变量——因为锁的是实例,而 static 属于类;
- 必须用 static synchronized 方法,或者 synchronized(YourClass.class) 代码块;
- 否则多个实例的线程会各自操作自己的副本(实际是同一份 static 数据,但锁不统一),导致竞态。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











