網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
什么是虛假喚醒
虛假喚醒是一種現(xiàn)象,它只會(huì)出現(xiàn)在多線程環(huán)境中,指的是在多線程環(huán)境下,多個(gè)線程等待在同一個(gè)條件上,等到條件滿足時(shí),所有等待的線程都被喚醒,但由于多個(gè)線程執(zhí)行的順序不同,后面競(jìng)爭(zhēng)到鎖的線程在獲得時(shí)間片時(shí)條件已經(jīng)不再滿足,線程應(yīng)該繼續(xù)睡眠但是卻繼續(xù)往下運(yùn)行的一種現(xiàn)象。
上面是比較書面化的定義,我們用人能聽懂的話來(lái)介紹一下虛假喚醒。
多線程環(huán)境的編程中,我們經(jīng)常遇到讓多個(gè)線程等待在一個(gè)條件上,等到這個(gè)條件成立的時(shí)候我們?cè)偃拘堰@些線程,讓它們接著往下執(zhí)行代碼的場(chǎng)景。假如某一時(shí)刻條件成立,所有的線程都被喚醒了,然后去競(jìng)爭(zhēng)鎖,因?yàn)橥粫r(shí)刻只會(huì)有一個(gè)線程能拿到鎖,其他的線程都會(huì)阻塞到鎖上無(wú)法往下執(zhí)行,等到成功爭(zhēng)搶到鎖的線程消費(fèi)完條件,釋放了鎖,后面的線程繼續(xù)運(yùn)行,拿到鎖時(shí)這個(gè)條件很可能已經(jīng)不滿足了,這個(gè)時(shí)候線程應(yīng)該繼續(xù)在這個(gè)條件上阻塞下去,而不應(yīng)該繼續(xù)執(zhí)行,如果繼續(xù)執(zhí)行了,就說(shuō)發(fā)生了虛假喚醒。
import threading
from threading import Condition
class Data:
def __init__(self, cond, num):
self.num = num
self.cond = cond
def add(self):
self.cond: Condition = self.cond
self.cond.acquire()
if self.num > 0:
self.cond.wait()
self.num += 1
print(threading.current_thread().getName(), self.num)
self.cond.notifyAll()
self.cond.release()
def decr(self):
self.cond: Condition = self.cond
self.cond.acquire()
if self.num == 0:
self.cond.wait()
self.num -= 1
print(threading.current_thread().getName(), self.num)
self.cond.notifyAll()
self.cond.release()
if __name__ == '__main__':
cond = Condition()
num = 0
data = Data(cond, 0)
thread_add = threading.Thread(name="A", target=data.add)
thread_decr = threading.Thread(name="B", target=data.decr)
thread_add.start()
thread_decr.start()
現(xiàn)在改用4個(gè)線程
import threading
from threading import Condition
class Data:
def __init__(self, cond, num):
self.num = num
self.cond = cond
def add(self):
self.cond: Condition = self.cond
self.cond.acquire()
if self.num > 0:
self.cond.wait()
self.num += 1
print(threading.current_thread().getName(), self.num)
self.cond.notifyAll()
self.cond.release()
def decr(self):
self.cond: Condition = self.cond
self.cond.acquire()
if self.num == 0:
self.cond.wait()
self.num -= 1
print(threading.current_thread().getName(), self.num)
self.cond.notifyAll()
self.cond.release()
if __name__ == '__main__':
cond = Condition()
num = 0
data = Data(cond, 0)
thread_add = threading.Thread(name="A", target=data.add)
thread_decr = threading.Thread(name="B", target=data.decr)
thread_add2 = threading.Thread(name="C", target=data.add)
thread_decr2 = threading.Thread(name="D", target=data.decr)
thread_add.start()
thread_decr.start()
thread_add2.start()
thread_decr2.start()
還沒有出現(xiàn)問題!!!
使用20個(gè)線程同時(shí)跑
import threading
from threading import Condition
class Data:
def __init__(self, cond, num):
self.num = num
self.cond = cond
def add(self):
self.cond: Condition = self.cond
self.cond.acquire()
if self.num > 0:
self.cond.wait()
self.num += 1
print(threading.current_thread().getName(), self.num)
self.cond.notifyAll()
self.cond.release()
def decr(self):
self.cond: Condition = self.cond
self.cond.acquire()
if self.num == 0:
self.cond.wait()
self.num -= 1
print(threading.current_thread().getName(), self.num)
self.cond.notifyAll()
self.cond.release()
if __name__ == '__main__':
cond = Condition()
num = 0
data = Data(cond, 0)
for i in range(10):
thread_add = threading.Thread(name="A", target=data.add)
thread_add.start()
for i in range(10):
thread_decr = threading.Thread(name="B", target=data.decr)
thread_decr.start()
這時(shí)就出現(xiàn)了問題!!!
現(xiàn)在改用while進(jìn)行判斷
防止虛假喚醒:
import threading
from threading import Condition
class Data:
def __init__(self, cond, num):
self.num = num
self.cond = cond
def add(self):
self.cond: Condition = self.cond
self.cond.acquire()
# 這里采用了while進(jìn)行判斷,防止虛假喚醒
while self.num > 0:
self.cond.wait()
self.num += 1
print(threading.current_thread().getName(), self.num)
self.cond.notifyAll()
self.cond.release()
def decr(self):
self.cond: Condition = self.cond
self.cond.acquire()
# 這里采用了while進(jìn)行判斷,防止虛假喚醒
while self.num == 0:
self.cond.wait()
self.num -= 1
print(threading.current_thread().getName(), self.num)
self.cond.notifyAll()
self.cond.release()
if __name__ == '__main__':
cond = Condition()
num = 0
data = Data(cond, 0)
for i in range(10):
thread_add = threading.Thread(name="A", target=data.add)
thread_add.start()
for i in range(10):
thread_decr = threading.Thread(name="B", target=data.decr)
thread_decr.start()
這個(gè)例子與上面的代碼幾乎沒有差別,只是把if判斷換成了while判斷,所以每次蕭炎和唐三醒過(guò)來(lái)之后都會(huì)再判斷一下有沒有蘋果(喚醒自己的條件是否滿足),如果不滿足,就會(huì)繼續(xù)睡下去,不會(huì)接著往下運(yùn)行,從而避免了虛假喚醒。
總結(jié)
等待在一個(gè)條件上的線程被全部喚醒后會(huì)去競(jìng)爭(zhēng)鎖,所以這些線程會(huì)一個(gè)一個(gè)地去消費(fèi)這個(gè)條件,等到后面的線程去消費(fèi)這個(gè)條件時(shí),條件可能已經(jīng)不滿足了,所以每個(gè)被喚醒的線程都需要再檢查一次條件是否滿足。如果不滿足,應(yīng)該繼續(xù)睡下去;只有滿足了才能往下執(zhí)行。
原文鏈接:https://wtl4it.blog.csdn.net/article/details/129213883
- 上一篇:沒有了
- 下一篇:沒有了
相關(guān)推薦
- 2022-08-25 Python中如何使用Matplotlib庫(kù)繪制圖形_python
- 2023-11-20 python實(shí)現(xiàn)ssh傳遞文件
- 2022-04-11 git 強(qiáng)行拉取覆蓋本地方法(Git強(qiáng)制覆蓋本地代碼)
- 2023-07-05 實(shí)際開發(fā)中如何存儲(chǔ)密碼(md5加鹽bcrypt)golang
- 2023-05-22 Redis數(shù)據(jù)結(jié)構(gòu)類型示例解析_Redis
- 2023-01-30 python第三方異步日志庫(kù)loguru簡(jiǎn)介_python
- 2022-09-21 三個(gè)Python自動(dòng)化辦公好用到爆的模塊分享_python
- 2023-01-02 Kotlin類對(duì)象class初始化與使用_Android
- 欄目分類
-
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支