线程安全
线程不安全示例
public class UnsafeTicketSeller {
// 总票数
private int tickets = 10;
// 售票方法
public void sellTicket() {
// 检查是否还有票
if (tickets > 0) {
// 模拟处理时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 卖票
tickets--;
System.out.println("Ticket sold. Tickets left: " + tickets);
} else {
System.out.println("No tickets available.");
}
}
public static void main(String[] args) {
UnsafeTicketSeller seller = new UnsafeTicketSeller();
// 创建多个线程尝试卖票
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
seller.sellTicket();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
seller.sellTicket();
}
});
t1.start();
t2.start();
}
}
sellTicket() 方法没做同步处理,两个线程可能同时检查 tickets > 0
线程不安全原因
- 可见性:线程处理一个数据时,会在线程内部缓存一个副本,每次操作都是修改线程自己内部的副本变量,修改后也不会回写主存。当多个线程操作时,就会导致不准确
- 原子性:一组代码操作一个共享变量,这组代码假设有 10行,线程A执行到第 5 行时 CPU 时间片没了,线程 A 暂停,线程 B 去执行,这是线程 B 执行完,线程 A 得到时间片,继续执行后面的 5 行,。比如简单的 i+=1 这个会被翻译成 3 个指令:读取 i、i+1、回写i
- 有序性:代码经过后端编译器优化后可能不是代码原本的顺序;CPU 在执行指令时,CPU 也可能自己去优化,优化后也可能和代码原本的顺序不一致
保证线程安全
也就是要处理 可见性、原子性、有序性,只要这三者处理好了,线程就安全了。有可能某个场景线程不安全的原因只是其中之一,比如上面的卖票例子,就是可见性问题导致的
synchronized 可以解决:可见性、原子性、有序性。上面的卖票示例, sellTicket() 方法使用 synchronized 修饰,可以保证线程安全
volatile 可以解决:可见性、有序性。上面的卖票示例,tickets 变量使用 volatile 修饰,可以保证线程安全
JUC 的锁机制也能保证线程安全,JUC 提供了一系列工具类给我们使用,原子类的变量、各种锁、线程安全的集合等,底层是 AQS 实现的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具