在编写多线程程序时,多个线程同时访问共享资源的情况很常见。比如一个家庭用的智能WiFi管理系统,可能有多个设备同时更新网络配置或读取连接状态。这时候如果不加控制,就容易出现数据错乱,就像两个家人同时按路由器重启键,结果谁也不知道当前到底有没有重启成功。
共享变量被覆盖
最常见的问题是多个线程同时修改同一个变量。例如,两个线程都读取了某个计数器值为5,各自加1后写回,结果本应是7,却还是6。这是因为没有对读-改-写的过程加锁,导致操作不原子。
int counter = 0;
// 多个线程中执行
counter++; // 非原子操作,可能出错
死锁是怎么发生的
两个线程各拿着一个锁,又等着对方释放另一个锁,结果谁都动不了。就像两个人过窄桥,对面来了也不退,卡在中间进退不得。这种情况在处理多个资源时特别容易出现,尤其是加锁顺序不一致的时候。
// 线程A
synchronized(lock1) {
synchronized(lock2) {
// 执行操作
}
}
// 线程B
synchronized(lock2) {
synchronized(lock1) {
// 执行操作
}
}
过度同步影响性能
有人觉得加锁越严越好,其实不然。把整个方法都锁住,就像为了防止别人抢网速,把WiFi密码每秒换一次,虽然安全了,但谁也连不上。合理的做法是只锁关键代码段,减少竞争。
忘记释放锁
使用显式锁(如ReentrantLock)时,必须手动释放。如果在try块里加锁,却忘了finally里unlock,一旦异常发生,锁就一直占着,其他线程全被堵住。这就像拔了路由器电源没再插回去,全家都断网。
lock.lock();
try {
// 操作共享资源
} finally {
lock.unlock(); // 必不可少
}
误以为volatile能替代synchronized
volatile能保证可见性,但不能保证原子性。用它来修饰count++这样的操作,依然会出问题。它适合用于状态标志位,比如“是否需要重新扫描WiFi信号”,但不适合做计数器。