在写程序的时候,尤其是处理多线程任务时,经常会遇到多个线程同时访问同一块数据的情况。比如你写了个抢票系统,两个人同时点击购买,结果票数变成负的了——这就是典型的资源竞争问题。这时候,锁机制就派上用场了。
什么是锁机制
锁机制的核心作用是保证在同一时间只有一个线程能访问某个共享资源。就像公共厕所的门锁,一次只能一个人进去,其他人得排队等。在编程中,我们通过加锁和释放锁来控制访问顺序,避免数据混乱。
Python 中的 threading.Lock 示例
Python 的 threading 模块提供了简单的锁支持。下面是一个模拟银行账户取款的例子:
import threading
balance = 1000 # 初始余额
lock = threading.Lock()
def withdraw(name, amount):
global balance
with lock: # 自动加锁,执行完自动释放
if balance >= amount:
print(f"{name} 正在取款 {amount}")
balance -= amount
print(f"余额还剩 {balance}")
else:
print(f"{name} 取款失败,余额不足!")
t1 = threading.Thread(target=withdraw, args=('小明', 600))
t2 = threading.Thread(target=withdraw, args=('小红', 800))
t1.start()
t2.start()
t1.join()
t2.join()
如果不加锁,两个线程可能同时读到 balance=1000,都判断可以取款,结果出现超支。加上 with lock 后,第二个线程必须等第一个执行完才能进入,数据就安全了。
Java 中的 synchronized 关键字
Java 提供了更简洁的语法。比如一个计数器类:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
这里的 synchronized 就相当于给方法上了锁,同一时间只允许一个线程调用。适合方法体不大、但需要保护共享状态的场景。
注意死锁问题
锁用得好是保护,用不好反而会卡住程序。比如两个线程各自拿着一把锁,又等着对方释放,就会僵持住。这就像两个人互相堵着门口,谁也不让,最后都进不去。
避免死锁的一个办法是统一加锁顺序。比如多个资源时,总是先锁 A 再锁 B,大家遵守同一个规则,就不会乱套。
使用建议
锁不是越多越好,过度加锁会影响性能。如果只是读操作,可以用读写锁(ReadWriteLock),允许多个读但互斥写。另外,现代语言也提供了原子操作、信号量等替代方案,按需选择更合适的方式。
实际开发中,比如做爬虫时限制并发请求数,或者写日志防止内容交错,锁机制都很实用。掌握这些小技巧,代码跑起来更稳当。