java多線程(7.0)

目錄

?編輯

定時器

定時器的使用?

三.定時器的實現 MyTimer

3.1 分析思路

1. 創建執行任務的類。

?2. 管理任務???

3. 執行任務

3.2 線程安全問題


?

定時器

定時器是軟件開發中的一個重要組件. 類似于一個 "鬧鐘". 達到一個設定的時間之后, 就執行某個指定好的代碼.

定時器的使用?

標準庫中的定時器 標準庫中提供了一個 Timer 類. Timer 類的核心方法為 schedule . schedule 包含兩個參數.

第一個參數是繼承timetask抽象類的類實例且內部重寫了run方法(這里的匿名類隱式繼承了TimerTask):指定即將要執行的任務代碼(timetask實現了runable接口所以有run方法)

?第二個參數指定多長時間之后執行 (單位為毫秒).

Timer timer = new Timer();
timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello");}
}, 3000);

執行schedule方法的時候,系統把要執行的任務放到timer對象中,與此同時timer對象里頭自帶一個線程叫做“掃描線程”,一旦時間到掃描線程就會執行剛才安排的任務,執行完所有任務后線程也不會銷毀,會阻塞等待直到其他的任務被放到timer對象中再繼續執行(就這么重復

三.定時器的實現 MyTimer

3.1 分析思路

對于定時器來說:

  1. 創建描述一個要執行的任務(任務內容 + 執行任務時間)的類
  2. 管理多個任務,通過一定的數據結構,把多個任務存起來
  3. 有專門的線程,執行這里的任務
1. 創建執行任務的類。

? ?我們在調用 schedule 時,傳的是延遲時間 “delay” 值。但是,描述任務時,不太建議使用 delay

表示,最好使用 “絕對時間”(時間戳)來表示~~

public class MyTimerTask implements Comparable<MyTimerTask>{//此處這里的 time,通過毫秒時間戳,表示這個任務具體啥時候執行private long time;private Runnable runnable;public  MyTimerTask(Runnable runnable,long delay){this.time = System.currentTimeMillis() + delay;this.runnable = runnable;}public void run(){runnable.run();}public long getTime(){return time;}@Overridepublic int compareTo(MyTimerTask o) {//比如,當前時間是 10:30,任務時間是 12:00,不應該執行//如果當前時間是 10:30,任務時間是 10:29,應該執行//誰減去誰,可以通過實驗判斷return (int) (this.time - o.time);}
}
?2. 管理任務???

使用 List 管理任務,不是一個好選擇——因為后續執行列表中的任務時,就需要依次遍歷每個元素;執行完畢后,還需要把對應的任務從 List 中刪除掉。???
我們需要按照時間來執行這里的任務。只要能夠確定所有任務中,時間最早的任務,判定它是否到該執行的時間即可。如果時間最早的任務還沒到執行時間,其他任務更不可能到時間了。因此,我們使用堆數據結構(涉及到隊列中的元素排序時,考慮堆)——PriorityQueue<MyTimerTask>(優先級隊列管理元素時,需要有比較方法,才能排序存儲。因此,在實現 MyTimerTask 類時,要繼承 Comparable<MyTimerTask> 接口,重寫 compareTo比較方法)

3. 執行任務

當創建 MyTimer 對象,調用無參構造方法時,便創建一個線程,循環執行從隊列中取出任務的操作:取出隊列中 “絕對時間” 最早的任務——如果當前時間 >= 此任務的時間(已經到達此任務的執行時間),便可調用run方法執行,執行完畢后從隊列中刪除; 如果當前時間 < 此任務的時間(沒到此任務的執行時間),則繼續執行循環。(所以,在實現 MyTimerTask 類時,要有 run方法 和 getTime方法)

? ?除此之外 ,還需要有 schedule 方法添加任務。

import java.util.PriorityQueue;public class MyTimer {private final PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public MyTimer() {Thread t = new Thread(() -> {while (true){if(queue.isEmpty()){ continue;}MyTimerTask task = queue.peek();//判斷是否滿足執行條件if (System.currentTimeMillis() >= task.getTime()) {task.run();//執行完后,便從隊列中刪除queue.poll();}else{continue;}}});t.start();}public void schedule(Runnable runnable, long delay) {MyTimerTask task = new MyTimerTask(runnable, delay);queue.offer(task);}
}
3.2 線程安全問題

? ?當前這個代碼,是沒有考慮線程安全問題的。

? ?PriorityQueue 這個類自身,是非線程安全的,并且又是多個線程來進行操作,一定存在線程安全問題的風險。因此,要在涉及隊列相關操作的地方加鎖。(讓刪的時候不能進行加入操作,加的時候不進行刪除操作)

import java.util.PriorityQueue;public class MyTimer {private final PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();private final Object locker = new Object();public MyTimer() {Thread t = new Thread(() -> {while (true){synchronized(locker){if(queue.isEmpty()){ continue;}MyTimerTask task = queue.peek();if (System.currentTimeMillis() >= task.getTime()) {task.run();queue.poll();}else{continue;}}}});t.start();}public void schedule(Runnable runnable, long delay) {synchronized(locker){MyTimerTask task = new MyTimerTask(runnable, delay);queue.offer(task);}}
}

?但是,加完鎖以后,又出現了線程安全問題。?

1)初始情況下,如果隊列中,沒有任何元素。此時,就會在短時間內執行大量循環,這樣的執行是沒有意義的,導致“線程餓死”。

因此,我們需要添加 wait 和 notify 機制:隊列為空時,進行等待;添加任務時,就喚醒線程。

2)假設隊列中,已經包含元素了,并且當前時間是 10:45,任務時間 12:00(類似于,我定了12:00的鬧鐘,現在是 10:45)。在判斷任務是否滿足執行條件時,不滿足就會一直循環(相當于每隔一會兒就看一眼鬧鐘),這樣無意義的執行就一直占用著cpu資源,導致 “線程餓死”。

因此,我們需要添加一個有等待期限的 wait(等待 1h15min 就會執行),當到達任務執行時間,wait 就結束了。如果在等待過程中,又再次調用 schedule 方法,也會喚醒這里的 wait,進行新一輪的判斷。

?線程安全版:

import java.util.PriorityQueue;public class MyTimer {private final PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();private final Object locker = new Object();public MyTimer() {Thread t = new Thread(() -> {try {while (true) {synchronized (locker) {while (queue.isEmpty()) {//如果還沒添加任務,會不斷循環執行判斷,出現線程餓死。//continue;//因此,使用wait等待,當添加任務后喚醒locker.wait();}MyTimerTask task = queue.peek();if (System.currentTimeMillis() >= task.getTime()) {task.run();queue.poll();} else {//如果還沒到任務執行時間,依舊不斷循環判斷,出現線程餓死。//continue;//因此,使用有等待期限的 wait,計算執行的時間與當前時間的差值//當添加新的任務后,wait 被喚醒,再進行新的判斷locker.wait(task.getTime() - System.currentTimeMillis());}}}} catch (InterruptedException e) {e.printStackTrace();}});t.start();}public void schedule(Runnable runnable, long delay) {synchronized (locker){MyTimerTask task = new MyTimerTask(runnable, delay);queue.offer(task);// 喚醒 waitlocker.notify();}}
}

* 能否將第二處的 wait 改為 sleep 呢? ——不能!!

?不應該使用sleep,可能存在以下情況:
?????1)在 sleep 阻塞1h15min 的過程中,新來了一個時間更早的任務,比如 11:30 要執行。如果使用 wait ,每次新來的任務,都會把 wait 喚醒,重新設定 wait 的等待時間。而 sleep 不會被喚醒,依舊在阻塞著.....
?????2)sleep 休眠的時候,不會釋放鎖。因此,在休眠的時候就是“抱著鎖”,其他人想拿鎖就拿不到了。也就是說你休眠的時候,就不能進行增加線程

?* PriorityQueue 是線程不安全的類,能否使用 PriorityBlockingQueue 線程安全的阻塞隊列呢?——不能!!????
如果使用線程安全的隊列,會導致代碼中從 一把鎖 變成 兩把鎖,很容易出現死鎖的情況,比如持有和請求的情況(并非100%一定出現,但是需要程序員精心控制加鎖順序,使得編寫代碼的復雜度提高了。如果通篇代碼 只有一把鎖,就能更容易地解決問題)

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/78152.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/78152.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/78152.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

優化非線性復雜系統的參數

非線性項組合的系統 對于系統中的每一個復雜擬合&#xff0c;即每一個殘差函數&#xff0c;都能表示為非線性方程的趨勢&#xff0c;例如較為復雜的系統函數組&#xff0c; from optimtool.base import sp, np x sp.symbols("x1:5") res1 0.5*x[0] 0.2*x[1] 1.…

清華LeapLab開源Cooragent框架:一句話構建本地智能體服務群,讓AGI真正觸手可及

引言&#xff1a;智能體革命&#xff0c;從復雜到簡單 在人工智能發展的浪潮中&#xff0c;Agent&#xff08;智能體&#xff09; 技術被視為實現通用人工智能&#xff08;AGI&#xff09;的關鍵路徑。然而&#xff0c;傳統智能體的開發與協作始終面臨兩大痛點&#xff1a;依賴…

云原生--核心組件-容器篇-1-Docker和云原生關系(Docker是云原生的基石)

1、基本概念 &#xff08;1&#xff09;、云原生&#xff08;Cloud Native&#xff09; 是一種構建和運行應用程序的方法論&#xff0c;旨在充分利用云計算環境&#xff08;公有云、私有云、混合云&#xff09;的特性&#xff0c;通過容器化、微服務、服務網格、聲明式API等技…

問答頁面支持拖拽和復制粘貼文件,MaxKB企業級AI助手v1.10.6 LTS版本發布

2025年4月24日&#xff0c;MaxKB開源企業級AI助手正式發布v1.10.6 LTS版本。這一版本主要進行了一些功能優化和問題修復。 功能優化 ■ 應用&#xff1a;文件上傳支持上傳其他自定義的文件類型&#xff0c;該類型文件需要自行寫入函數解析&#xff1b; ■ 問答頁面&#xff…

用戶案例--慧眼科技

作者&#xff1a;算力魔方創始人/英特爾創新大使劉力 每個行業都有其獨特的需求&#xff0c;算力魔方推出了全面的定制化服務&#xff0c;從概念到產品化&#xff0c;滿足各行各業&#xff0c;用戶可以根據具體應用需求定制更多接口或更強圖形處理的需求&#xff0c;且算力魔方…

apple 個人開發者轉公司經驗

1、在apple開發者官網申請 2、收到郵件后&#xff0c;回復準備了開始遷移 3、收到填寫遷移資料的郵件 4、開始填寫資料 Sign In - Applehttps://developer.apple.com/enroll/type/edit To complete this change, you will need: 要完成此更改&#xff0c;您需要&#xff1a; L…

【ESP32-IDF筆記】20-配置以太網網絡(W5500)

環境配置 Visual Studio Code &#xff1a;版本1.98.2 ESP32&#xff1a;ESP32-S3 ESP-IDF&#xff1a;V5.4 模塊&#xff1a;W5500&#xff0c;SPI通訊協議 組件支持&#xff1a;esp_eth 官方的ethernet 以太網組件 W5500介紹 介紹 W5500 是一款全硬件 TCP/IP 嵌入式以太網…

衛星通信的基本概念

1 頻段 頻段 頻率范圍 技術特點 典型應用 優勢 局限性 最新進展 L 頻段 1-2 GHz 波長較長&#xff0c;穿透能力強&#xff0c;受天氣影響小&#xff0c;帶寬較窄&#xff08;<100 MHz&#xff09;。 衛星導航&#xff08;北斗 / GPS&#xff09;、海事通信&#x…

數據結構------C語言經典題目(7)

1.系統棧和數據結構中的棧有什么區別&#xff1f; 1.本質&#xff1a; 系統棧&#xff1a;由程序運行時由操作系統自動分配的一塊連續內存區域&#xff0c;用于存儲函數調用過程中的臨時數據&#xff08;參數、局部變量、返回地址&#xff09;&#xff0c;是程序運行的底層機制…

【Redis】一、redis的下載與安裝

目錄 一、redis下載 二、啟動服務 三、測試服務 四、可視化界面 五、設置reids密碼 今天起準備對redis進行學習&#xff0c;目標是掌握實際開發項目中如何應用redis等操作。首先在這里講將如何下載redis&#xff0c;方便以后查閱。 一、redis下載 可以去官網&#xff08…

vue3中nextTick的作用及示例

在Vue 3中&#xff0c;nextTick是一個用于處理DOM異步更新的工具函數&#xff0c;確保在數據變化后操作最新的DOM。以下是其作用的詳細解析&#xff1a; 核心作用 延遲回調到DOM更新后&#xff1a;Vue的響應式系統會將數據變更批量處理&#xff0c;異步更新DOM。nextTick允許你…

拆解大模型“越獄”攻擊:對抗樣本如何撕開AI安全護欄?

該文章首發于奇安信攻防社區:https://forum.butian.net/share/4254 引言 隨著大規模語言模型(LLMs)在內容生成、智能交互等領域的廣泛應用,其安全性和可控性成為學界和產業界關注的焦點。盡管主流模型通過道德對齊機制建立了安全護欄,但研究者發現,通過精心設計的"…

Ubuntu主機上通過WiFi轉有線為其他設備提供網絡連接

以下是在Ubuntu主機上通過WiFi轉有線為Jetson設備提供網絡連接的步驟&#xff1a; ??1. 確認網絡接口名稱?? 在Ubuntu主機上執行以下命令&#xff0c;查看WiFi和有線接口名稱&#xff1a; ip a WiFi接口通常類似 wlp2s0 或 wlan0有線接口通常類似 enp0s25 或 eth0 記下…

通訊錄完善版本(詳細講解+源碼)

目錄 前言 一、使通訊可以動態更新內存 1、contact.h 2、contact.c 存信息&#xff1a; 刪除聯系人&#xff0c;并試一個不存在的人的信息&#xff0c;看看會不會把其他人刪了 ?編輯 修改&#xff1a; ?編輯 排序&#xff1a; ?編輯 銷毀&#xff1a; ?編輯 ?…

Linux操作系統復習

Linux操作系統復習 一. Linux的權限和shell原理1. Linux從廣義上講是什么 從狹義上講是什么&#xff1f;2. shell是什么&#xff1f;3. 為什么要設置一個shell外殼而不是直接和linux 內核溝通4. shell的原理是什么5. Linux中權限的概念6. 如何提升當前操作的權限7. 文件訪問者的…

Spring AI 快速入門:從環境搭建到核心組件集成

Spring AI 快速入門&#xff1a;從環境搭建到核心組件集成 一、前言&#xff1a;Java開發者的AI開發捷徑 對于Java生態的開發者來說&#xff0c;將人工智能技術融入企業級應用往往面臨技術棧割裂、依賴管理復雜、多模型適配困難等挑戰。Spring AI的出現徹底改變了這一局面——…

C++11介紹

目錄 一、C11的兩個小點 1.1、decltype 1.2、nullptr 二、列表初始化 2.1、C98傳統的{} 2.2、C11中的{} 2.3、C11中的std::initializer_list 三、右值引用和移動語義 3.1、左值和右值 3.2、左值引用和右值引用 3.3、引用延長生命周期 3.4、左值和右值的參數匹配 3…

基于機器學習的網絡釣魚郵件智能檢測與防護系統

phishingDP 介紹 phishingDP 是一個基于機器學習的網絡釣魚郵件智能檢測與防護系統&#xff0c;旨在通過深度學習技術識別潛在的釣魚郵件&#xff0c;保護用戶免受網絡詐騙威脅。該系統集成了數據預處理、模型訓練、實時預測和結果可視化功能&#xff0c;提供用戶友好的Web界…

OpenAI 推出「輕量級」Deep Research,免費用戶同享

剛剛&#xff0c;OpenAI 正式上線了面向所有用戶的「輕量級」Deep Research 版本&#xff0c;意味著即便沒有付費訂閱&#xff0c;也能體驗這一強大工具的核心功能。 核心差異&#xff1a;o4-mini vs. o3 模型迭代 傳統的深度研究功能基于更大規模的 o3 模型。輕量級版本則改以…

什么是優質的靜態IP?以及如何選擇優質的靜態IP?

在如今的大數據生態中&#xff0c;靜態IP的使用頻率和重要性不斷提升。但是&#xff0c;我們常聽到業界提到“優質的靜態IP”&#xff0c;那么什么樣的靜態IP能夠稱之為優質&#xff1f;如何判斷這些IP能否滿足我們的需求&#xff1f;今天這篇文章&#xff0c;將為您揭開優質靜…