(一个并发编程中线程同步和同步锁的问题)
1. 线程安全问题
- 经典问题——银行取钱问题里面的核心问题
- (1)用户输入账户、密码并判断
- (2)输入取款金额
- (3)判断余额与取款金额孰多孰少
- (4)如果余额大于取款金额则取款成功;否则失败
- 若是代码如下:
import threading
import time
class Account:
# 初始化构造器
def __init__(self, account_no, balance):
# 封装账户编号和账户余额两个成员变量
self.account_no = account_no
self.balance = balance
# 提供模拟取钱的draw方法来完成取钱操作
def draw(self, account, draw_amount):
# 账户余额必须大于取钱数目
if account.balance >= draw_amount:
# 取出钞票的数额
print(threading.current_thread().name + " 取钱成功!吐出钞票:" + str(draw_amount))
# 模拟其它线程切换进来,注释掉也有机会出现
time.sleep(0.0000000000000000000001)
# 修改余额
account.balance -= draw_amount
print("余额为:" + str(account.balance))
else:
print(threading.current_thread().name + " 取钱失败,余额不足!")
if __name__ == '__main__':
# create a Account
acct = Account("1234567", 1000)
threading.Thread(target=acct.draw, args=(acct, 800)).start()
# 第一个进行的时候,第二个也有可能抢进来
threading.Thread(target=acct.draw, args=(acct, 800)).start()
- 会出现如下问题:
- 所以用同步锁的方法,解决。
2. 同步锁(Lock)
import threading
import time
class Account:
# 初始化构造器
def __init__(self, account_no, balance):
# 封装账户编号和账户余额两个成员变量
self.account_no = account_no
self._balance = balance
self.lock = threading.RLock()
# 因为账户余额不允许随便修改,所以只为self._balance提供getter方法
def getBalance(self):
return self._balance
#提供一个线程安全的draw方法来完成取钱操作
def draw(self, account, draw_amount):
# 加锁
self.lock.acquire()
try:
if account._balance >= draw_amount:
# 这里在_balance是指私有变量,只能由类对象和子类对象修改
# 取出钞票的数额
print(threading.current_thread().name + " 取钱成功!吐出钞票:" + str(draw_amount))
time.sleep(0.00000000000001)
# 修改余额
account._balance -= draw_amount
print("余额为:" + str(account._balance))
else:
print(threading.current_thread().name + " 取钱失败,余额不足!")
finally:
# 修改完成,释放锁
self.lock.release()
if __name__ == '__main__':
# create a Account
acct = Account("1234567", 1000)
threading.Thread(target=acct.draw, args=(acct, 800)).start()
# 第一个完成不了,第二个就没法完成
threading.Thread(target=acct.draw, args=(acct, 800)).start()
- 上面加锁之后,同一个类对象的加锁的方法只由一个线程完成,解锁之后,其它线程才可以使用。