網站首頁 編程語言 正文
什么是虛假喚醒
虛假喚醒是一種現象,它只會出現在多線程環境中,指的是在多線程環境下,多個線程等待在同一個條件上,等到條件滿足時,所有等待的線程都被喚醒,但由于多個線程執行的順序不同,后面競爭到鎖的線程在獲得時間片時條件已經不再滿足,線程應該繼續睡眠但是卻繼續往下運行的一種現象。
上面是比較書面化的定義,我們用人能聽懂的話來介紹一下虛假喚醒。
多線程環境的編程中,我們經常遇到讓多個線程等待在一個條件上,等到這個條件成立的時候我們再去喚醒這些線程,讓它們接著往下執行代碼的場景。假如某一時刻條件成立,所有的線程都被喚醒了,然后去競爭鎖,因為同一時刻只會有一個線程能拿到鎖,其他的線程都會阻塞到鎖上無法往下執行,等到成功爭搶到鎖的線程消費完條件,釋放了鎖,后面的線程繼續運行,拿到鎖時這個條件很可能已經不滿足了,這個時候線程應該繼續在這個條件上阻塞下去,而不應該繼續執行,如果繼續執行了,就說發生了虛假喚醒。
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()
現在改用4個線程
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()
還沒有出現問題!!!
使用20個線程同時跑
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()
這時就出現了問題!!!
現在改用while進行判斷
防止虛假喚醒:
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進行判斷,防止虛假喚醒
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進行判斷,防止虛假喚醒
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()
這個例子與上面的代碼幾乎沒有差別,只是把if判斷換成了while判斷,所以每次蕭炎和唐三醒過來之后都會再判斷一下有沒有蘋果(喚醒自己的條件是否滿足),如果不滿足,就會繼續睡下去,不會接著往下運行,從而避免了虛假喚醒。
總結
等待在一個條件上的線程被全部喚醒后會去競爭鎖,所以這些線程會一個一個地去消費這個條件,等到后面的線程去消費這個條件時,條件可能已經不滿足了,所以每個被喚醒的線程都需要再檢查一次條件是否滿足。如果不滿足,應該繼續睡下去;只有滿足了才能往下執行。
原文鏈接:https://wtl4it.blog.csdn.net/article/details/129213883
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-09-04 Go語言流程控制語句_Golang
- 2022-04-28 WPF簡介與基礎開發_實用技巧
- 2022-05-29 解決Docker容器下不能使用vim命令的問題_docker
- 2022-10-12 Xshell7遠程連接失敗(connection?failed)的問題解決_Linux
- 2023-07-02 C++中默認無參構造函數的工作機制淺析_C 語言
- 2023-03-01 shell耗時計算的實現_linux shell
- 2022-06-25 Docker核心組件之聯合文件系統詳解_docker
- 2022-07-11 Python字節碼與程序執行過程詳解_python
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支