線程join_Java 并發編程:線程間的協作(wait/notify/sleep/yield/join)

點擊上方“Coder編程”,選擇“置頂公眾號”

技術文章第一時間送達!

0a35221b040e39c2da8fc50858d1cb85.png
并發編程.png

每天進步一點,不做curd工程師與Api調用工程師 歡迎訪問

個人博客網站:https://www.coder-programming.cn/?

?作者:liuxiaopeng? | http://www.cnblogs.com/paddix

一、線程的狀態

Java中線程中狀態可分為五種:New(新建狀態),Runnable(就緒狀態),Running(運行狀態),Blocked(阻塞狀態),Dead(死亡狀態)。

  • New:新建狀態,當線程創建完成時為新建狀態,即new Thread(...),還沒有調用start方法時,線程處于新建狀態。

  • Runnable:就緒狀態,當調用線程的的start方法后,線程進入就緒狀態,等待CPU資源。處于就緒狀態的線程由Java運行時系統的線程調度程序(thread scheduler)來調度。

  • Running:運行狀態,就緒狀態的線程獲取到CPU執行權以后進入運行狀態,開始執行run方法。

  • Blocked:阻塞狀態,線程沒有執行完,由于某種原因(如,I/O操作等)讓出CPU執行權,自身進入阻塞狀態。

  • Dead:死亡狀態,線程執行完成或者執行過程中出現異常,線程就會進入死亡狀態。

這五種狀態之間的轉換關系如下圖所示:

d8b43cb4c8225e5ae1a9f001f056f54c.png
image

有了對這五種狀態的基本了解,現在我們來看看Java中是如何實現這幾種狀態的轉換的。

二、wait/notify/notifyAll方法的使用

1、wait方法:

bc8490832c45997ab9461cca3a16930c.png
方法.png

JDK中一共提供了這三個版本的方法,

(1)wait()方法的作用是將當前運行的線程掛起(即讓其進入阻塞狀態),直到notify或notifyAll方法來喚醒線程.

(2)wait(long timeout),該方法與wait()方法類似,唯一的區別就是在指定時間內,如果沒有notify或notifAll方法的喚醒,也會自動喚醒。

(3)至于wait(long timeout,long nanos),本意在于更精確的控制調度時間,不過從目前版本來看,該方法貌似沒有完整的實現該功能,其源碼(JDK1.8)如下:

public?final?void?wait(long?timeout,?int?nanos)?throws?InterruptedException?{
????????if?(timeout?0)?{
????????????throw?new?IllegalArgumentException("timeout?value?is?negative");
????????}

????????if?(nanos?0?||?nanos?>?999999)?{
????????????throw?new?IllegalArgumentException(
????????????????????????????????"nanosecond?timeout?value?out?of?range");
????????}

????????if?(nanos?>=?500000?||?(nanos?!=?0?&&?timeout?==?0))?{
????????????timeout++;
????????}

????????wait(timeout);
????}
	從源碼來看,JDK8中對納秒的處理,只做了四舍五入,所以還是按照毫秒來處理的,可能在未來的某個時間點會用到納秒級別的精度。雖然JDK提供了這三個版本,其實最后都是調用`wait(long timeout)`方法來實現的,wait()方法與wait(0)等效,而`wait(long timeout,int nanos)`從上面的源碼可以看到也是通過`wait(long timeout)`來完成的。下面我們通過一個簡單的例子來演示wait()方法的使用:
package?com.paddx.test.concurrent;

public?class?WaitTest?{

????public?void?testWait(){
????????System.out.println("Start-----");
????????try?{
????????????wait(1000);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????System.out.println("End-------");
????}

????public?static?void?main(String[]?args)?{
????????final?WaitTest?test?=?new?WaitTest();
????????new?Thread(new?Runnable()?{
????????????@Override
????????????public?void?run()?{
????????????????test.testWait();
????????????}
????????}).start();
????}
}

這段代碼的意圖很簡單,就是程序執行以后,讓其暫停一秒,然后再執行。運行上述代碼,查看結果:

Start-----
Exception?in?thread?"Thread-0"?java.lang.IllegalMonitorStateException
????at?java.lang.Object.wait(Native?Method)
????at?com.paddx.test.concurrent.WaitTest.testWait(WaitTest.java:8)
????at?com.paddx.test.concurrent.WaitTest$1.run(WaitTest.java:20)
????at?java.lang.Thread.run(Thread.java:745)

這段程序并沒有按我們的預期輸出相應結果,而是拋出了一個異常。大家可能會覺得奇怪為什么會拋出異常?而拋出的IllegalMonitorStateException異常又是什么?我們可以看一下JDK中對IllegalMonitorStateException的描述:

Thrown?to?indicate?that?a?thread?has?attempted?to?wait?on?an?object's?monitor?or?to?notify?other?threads?waiting?on?an?object's?monitor?without?owning?the?specified?monitor.

這句話的意思大概就是:線程試圖等待對象的監視器或者試圖通知其他正在等待對象監視器的線程,但本身沒有對應的監視器的所有權。其實這個問題在《Java并發編程:Synchronized及其實現原理》一文中有提到過,wait方法是一個本地方法,其底層是通過一個叫做監視器鎖的對象來完成的。所以上面之所以會拋出異常,是因為在調用wait方式時沒有獲取到monitor對象的所有權,那如何獲取monitor對象所有權?Java中只能通過Synchronized關鍵字來完成,修改上述代碼,增加Synchronized關鍵字:

package?com.paddx.test.concurrent;

public?class?WaitTest?{

????public?synchronized?void?testWait(){//增加Synchronized關鍵字
????????System.out.println("Start-----");
????????try?{
????????????wait(1000);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????System.out.println("End-------");
????}

????public?static?void?main(String[]?args)?{
????????final?WaitTest?test?=?new?WaitTest();
????????new?Thread(new?Runnable()?{
????????????@Override
????????????public?void?run()?{
????????????????test.testWait();
????????????}
????????}).start();
????}
}

現在再運行上述代碼,就能看到預期的效果了:

Start-----
End-------

所以,通過這個例子,大家應該很清楚,wait方法的使用必須在同步的范圍內,否則就會拋出IllegalMonitorStateException異常,wait方法的作用就是阻塞當前線程等待notify/notifyAll方法的喚醒,或等待超時后自動喚醒。

2、notify/notifyAll方法

e0f7b031054d32d9bcfe6b0a6924a593.png
notify方法.png

有了對wait方法原理的理解,notify方法和notifyAll方法就很容易理解了。既然wait方式是通過對象的monitor對象來實現的,所以只要在同一對象上去調用notify/notifyAll方法,就可以喚醒對應對象monitor上等待的線程了。notify和notifyAll的區別在于前者只能喚醒monitor上的一個線程,對其他線程沒有影響,而notifyAll則喚醒所有的線程,看下面的例子很容易理解這兩者的差別:

package?com.paddx.test.concurrent;

public?class?NotifyTest?{
????public?synchronized?void?testWait(){
????????System.out.println(Thread.currentThread().getName()?+"?Start-----");
????????try?{
????????????wait(0);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????System.out.println(Thread.currentThread().getName()?+"?End-------");
????}

????public?static?void?main(String[]?args)?throws?InterruptedException?{
????????final?NotifyTest?test?=?new?NotifyTest();
????????for(int?i=0;i<5;i++)?{
????????????new?Thread(new?Runnable()?{
????????????????@Override
????????????????public?void?run()?{
????????????????????test.testWait();
????????????????}
????????????}).start();
????????}

????????synchronized?(test)?{
????????????test.notify();
????????}
????????Thread.sleep(3000);
????????System.out.println("-----------分割線-------------");

????????synchronized?(test)?{
????????????test.notifyAll();
????????}
????}
}

輸出結果如下:

Thread-0?Start-----
Thread-1?Start-----
Thread-2?Start-----
Thread-3?Start-----
Thread-4?Start-----
Thread-0?End-------
-----------分割線-------------
Thread-4?End-------
Thread-3?End-------
Thread-2?End-------
Thread-1?End-------

從結果可以看出:調用notify方法時只有線程Thread-0被喚醒,但是調用notifyAll時,所有的線程都被喚醒了。

最后,有兩點點需要注意:

(1)調用wait方法后,線程是會釋放對monitor對象的所有權的。

(2)一個通過wait方法阻塞的線程,必須同時滿足以下兩個條件才能被真正執行:

  • 線程需要被喚醒(超時喚醒或調用notify/notifyll)。
  • 線程喚醒后需要競爭到鎖(monitor)。

三、sleep/yield/join方法解析

上面我們已經清楚了wait和notify方法的使用和原理,現在我們再來看另外一組線程間協作的方法。這組方法跟上面方法的最明顯區別是:這幾個方法都位于Thread類中,而上面三個方法都位于Object類中。至于為什么,大家可以先思考一下。現在我們逐個分析sleep/yield/join方法:

1、sleep

sleep方法的作用是讓當前線程暫停指定的時間(毫秒),sleep方法是最簡單的方法,在上述的例子中也用到過,比較容易理解。唯一需要注意的是其與wait方法的區別。最簡單的區別是,wait方法依賴于同步,而sleep方法可以直接調用。而更深層次的區別在于sleep方法只是暫時讓出CPU的執行權,并不釋放鎖。而wait方法則需要釋放鎖。

package?com.paddx.test.concurrent;

public?class?SleepTest?{
????public?synchronized?void?sleepMethod(){
????????System.out.println("Sleep?start-----");
????????try?{
????????????Thread.sleep(1000);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????System.out.println("Sleep?end-----");
????}

????public?synchronized?void?waitMethod(){
????????System.out.println("Wait?start-----");
????????synchronized?(this){
????????????try?{
????????????????wait(1000);
????????????}?catch?(InterruptedException?e)?{
????????????????e.printStackTrace();
????????????}
????????}
????????System.out.println("Wait?end-----");
????}

????public?static?void?main(String[]?args)?{
????????final?SleepTest?test1?=?new?SleepTest();

????????for(int?i?=?0;i<3;i++){
????????????new?Thread(new?Runnable()?{
????????????????@Override
????????????????public?void?run()?{
????????????????????test1.sleepMethod();
????????????????}
????????????}).start();
????????}


????????try?{
????????????Thread.sleep(10000);//暫停十秒,等上面程序執行完成
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????System.out.println("-----分割線-----");

????????final?SleepTest?test2?=?new?SleepTest();

????????for(int?i?=?0;i<3;i++){
????????????new?Thread(new?Runnable()?{
????????????????@Override
????????????????public?void?run()?{
????????????????????test2.waitMethod();
????????????????}
????????????}).start();
????????}

????}
}

執行結果:


Sleep?start-----
Sleep?end-----
Sleep?start-----
Sleep?end-----
Sleep?start-----
Sleep?end-----
-----分割線-----
Wait?start-----
Wait?start-----
Wait?start-----
Wait?end-----
Wait?end-----
Wait?end-----

這個結果的區別很明顯,通過sleep方法實現的暫停,程序是順序進入同步塊的,只有當上一個線程執行完成的時候,下一個線程才能進入同步方法,sleep暫停期間一直持有monitor對象鎖,其他線程是不能進入的。而wait方法則不同,當調用wait方法后,當前線程會釋放持有的monitor對象鎖,因此,其他線程還可以進入到同步方法,線程被喚醒后,需要競爭鎖,獲取到鎖之后再繼續執行。

2、yield方法  yield方法的作用是暫停當前線程,以便其他線程有機會執行,不過不能指定暫停的時間,并且也不能保證當前線程馬上停止。yield方法只是將Running狀態轉變為Runnable狀態。我們還是通過一個例子來演示其使用:

package?com.paddx.test.concurrent;

public?class?YieldTest?implements?Runnable?{
????@Override
????public?void?run()?{
????????try?{
????????????Thread.sleep(100);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????for(int?i=0;i<5;i++){
????????????System.out.println(Thread.currentThread().getName()?+?":?"?+?i);
????????????Thread.yield();
????????}
????}

????public?static?void?main(String[]?args)?{
????????YieldTest?runn?=?new?YieldTest();
????????Thread?t1?=?new?Thread(runn,"FirstThread");
????????Thread?t2?=?new?Thread(runn,"SecondThread");

????????t1.start();
????????t2.start();

????}
}

運行結果如下:


FirstThread:?0
SecondThread:?0
FirstThread:?1
SecondThread:?1
FirstThread:?2
SecondThread:?2
FirstThread:?3
SecondThread:?3
FirstThread:?4
SecondThread:?4

這個例子就是通過yield方法來實現兩個線程的交替執行。不過請注意:這種交替并不一定能得到保證,源碼中也對這個問題進行說明:


/**
?????*?A?hint?to?the?scheduler?that?the?current?thread?is?willing?to?yield
?????*?its?current?use?of?a?processor.?The?scheduler?is?free?to?ignore?this
?????*?hint.
?????*
?????*?

?Yield?is?a?heuristic?attempt?to?improve?relative?progression
?????*?between?threads?that?would?otherwise?over-utilise?a?CPU.?Its?use
?????*?should?be?combined?with?detailed?profiling?and?benchmarking?to
?????*?ensure?that?it?actually?has?the?desired?effect.
?????*
?????*?

?It?is?rarely?appropriate?to?use?this?method.?It?may?be?useful
?????*?for?debugging?or?testing?purposes,?where?it?may?help?to?reproduce
?????*?bugs?due?to?race?conditions.?It?may?also?be?useful?when?designing
?????*?concurrency?control?constructs?such?as?the?ones?in?the
?????*?{@link?java.util.concurrent.locks}?package.
*/


這段話主要說明了三個問題:

  • 調度器可能會忽略該方法。
  • 使用的時候要仔細分析和測試,確保能達到預期的效果。
  • 很少有場景要用到該方法,主要使用的地方是調試和測試。

3、join方法

ce1c9f6273bde21bfaa537edbd2d8725.png
join方法.png

join方法的作用是父線程等待子線程執行完成后再執行,換句話說就是將異步執行的線程合并為同步的線程。JDK中提供三個版本的join方法,其實現與wait方法類似,join()方法實際上執行的join(0),而join(long millis, int nanos)也與wait(long millis, int nanos)的實現方式一致,暫時對納秒的支持也是不完整的。我們可以看下join方法的源碼,這樣更容易理解:

public?final?void?join()?throws?InterruptedException?{
????????join(0);
????}

?public?final?synchronized?void?join(long?millis)throws?InterruptedException?{
????????long?base?=?System.currentTimeMillis();
????????long?now?=?0;

????????if?(millis?0)?{
????????????throw?new?IllegalArgumentException("timeout?value?is?negative");
????????}

????????if?(millis?==?0)?{
????????????while?(isAlive())?{
????????????????wait(0);
????????????}
????????}?else?{
????????????while?(isAlive())?{
????????????????long?delay?=?millis?-?now;
????????????????if?(delay?<=?0)?{
????????????????????break;
????????????????}
????????????????wait(delay);
????????????????now?=?System.currentTimeMillis()?-?base;
????????????}
????????}
????}

public?final?synchronized?void?join(long?millis,?int?nanos)throws?InterruptedException?{

????????if?(millis?0)?{
????????????throw?new?IllegalArgumentException("timeout?value?is?negative");
????????}

????????if?(nanos?0?||?nanos?>?999999)?{
????????????throw?new?IllegalArgumentException(
????????????????????????????????"nanosecond?timeout?value?out?of?range");
????????}

????????if?(nanos?>=?500000?||?(nanos?!=?0?&&?millis?==?0))?{
????????????millis++;
????????}

????????join(millis);
????}

大家重點關注一下join(long millis)方法的實現,可以看出join方法就是通過wait方法來將線程的阻塞,如果join的線程還在執行,則將當前線程阻塞起來,直到join的線程執行完成,當前線程才能執行。不過有一點需要注意,這里的join只調用了wait方法,卻沒有對應的notify方法,原因是Thread的start方法中做了相應的處理,所以當join的線程執行完成以后,會自動喚醒主線程繼續往下執行。下面我們通過一個例子來演示join方法的作用:

(1)不使用join方法:

package?com.paddx.test.concurrent;

public?class?JoinTest?implements?Runnable{
????@Override
????public?void?run()?{

????????try?{
????????????System.out.println(Thread.currentThread().getName()?+?"?start-----");
????????????Thread.sleep(1000);
????????????System.out.println(Thread.currentThread().getName()?+?"?end------");
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????}

????public?static?void?main(String[]?args)?{
????????for?(int?i=0;i<5;i++)?{
????????????Thread?test?=?new?Thread(new?JoinTest());
????????????test.start();
????????}

????????System.out.println("Finished~~~");
????}
}

執行結果如下:

Thread-0?start-----
Thread-1?start-----
Thread-2?start-----
Thread-3?start-----
Finished~~~
Thread-4?start-----
Thread-2?end------
Thread-4?end------
Thread-1?end------
Thread-0?end------
Thread-3?end------

(2)使用join方法:

package?com.paddx.test.concurrent;

public?class?JoinTest?implements?Runnable{
????@Override
????public?void?run()?{

????????try?{
????????????System.out.println(Thread.currentThread().getName()?+?"?start-----");
????????????Thread.sleep(1000);
????????????System.out.println(Thread.currentThread().getName()?+?"?end------");
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????}

????public?static?void?main(String[]?args)?{
????????for?(int?i=0;i<5;i++)?{
????????????Thread?test?=?new?Thread(new?JoinTest());
????????????test.start();
????????????try?{
????????????????test.join();?//調用join方法
????????????}?catch?(InterruptedException?e)?{
????????????????e.printStackTrace();
????????????}
????????}

????????System.out.println("Finished~~~");
????}
}

執行結果如下:


Thread-0?start-----
Thread-0?end------
Thread-1?start-----
Thread-1?end------
Thread-2?start-----
Thread-2?end------
Thread-3?start-----
Thread-3?end------
Thread-4?start-----
Thread-4?end------
Finished~~~

對比兩段代碼的執行結果很容易發現,在沒有使用join方法之間,線程是并發執行的,而使用join方法后,所有線程是順序執行的。

四、總結

本文主要詳細講解了wait/notify/notifyAll和sleep/yield/join方法。最后回答一下上面提出的問題:wait/notify/notifyAll方法的作用是實現線程間的協作,那為什么這三個方法不是位于Thread類中,而是位于Object類中?位于Object中,也就相當于所有類都包含這三個方法(因為Java中所有的類都繼承自Object類)。要回答這個問題,還是得回過來看wait方法的實現原理,大家需要明白的是,wait等待的到底是什么東西?如果對上面內容理解的比較好的話,我相信大家應該很容易知道wait等待其實是對象monitor,由于Java中的每一個對象都有一個內置的monitor對象,自然所有的類都理應有wait/notify方法。

相關文章

  • Java 并發編程:核心理論

  • Java并發編程:Synchronized及其實現原理

  • Java并發編程:Synchronized底層優化(輕量級鎖、偏向鎖)

文末

文章收錄至 Github:?https://github.com/CoderMerlin/coder-programmingGitee:?https://gitee.com/573059382/coder-programming歡迎關注并star~

a8e20ffe5ffe6cf8abd0975cb9bfd9b0.png
微信公眾號

? ? ? ? ? ? ? ? ? 我知道你 “在看84a2ea42e3edae13322679b5c221411d.gif

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

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

相關文章

4位先行進位電路 logisim_你真的了解74系列集成電路嗎?讓我幫你總結一下

74系列集成電路大致可分為6大類&#xff1a;74(標準型)74LS(低功耗肖特基)74S(肖特基)74ALS(先進低功耗肖特基)74AS(先進肖特基)74F(高速)HC為COMS工作電平&#xff1b;HCT為TTL工作電平&#xff0c;可與74LS系列互換使用HCU適用于無緩沖級的CMOS電路。這9種74系列產品&#xf…

android ios av tv,iOS使用AVPlayer制作戰旗TvDEMO OC版

在視頻教學中看到一個別人寫的戰旗TvDEMO寫得很好&#xff0c;于是自己動手試著制作一個。效果如下圖所示主界面豎屏播放橫屏播放點擊出現工具欄視頻播放使用的是AVPlayer,具體的使用方式如下//---PlayView---_playView [[UIView alloc]init];_playView.frame CGRectMake(0, …

matlab 子圖title的位置_matlab 畫圖基本介紹

1.在命令窗口輸入命令時&#xff0c;可以不必每輸入一條命令就按enter鍵執行&#xff0c;可以在輸入幾行后一同運行。方法是&#xff1a;換行時&#xff0c;只要在按住<shift>鍵的同時按<enter>鍵即可&#xff0c;否則matlab就會執行上面輸入的所有語句。2.如何將數…

python程序打包成安卓app教程_Python打包方法基本應用方式介紹

Python編程語言是一款功能強大的免費開源的通用型計算機程序應用語言&#xff0c;對于經驗豐富的開發人員來說&#xff0c;掌握這樣一款語言在其程序開發中是一個比較重要的選擇。我們今天就先來了解一下python打包方法的相關應用。 一、簡介 py2exe是一個將Python腳本轉換成wi…

android添加刪除項目,編寫android計算器添加刪除按鈕,出現很抱歉,XX項目已停止運行。...

這是刪除按鈕監聽代碼&#xff1a;//del 按鈕的監聽btnDel (Button) findViewById(R.id.delete);btnDel.setOnClickListener(new OnClickListener() {Overridepublic void onClick(View view) {btnDel.setOnClickListener(this);//短按//btnDel.setOnLongClickListener(this);…

二元一次方程有唯一解的條件_人教版初中數學七年級下冊用適當方法解二元一次方程組公開課優質課課件教案視頻...

解二元一次方程組---加減法一、教學目的&#xff1a;1&#xff0e;使學生掌握用加減法解二元一次方程組的步驟。2&#xff0e;熟練運用加減法解二元一次方程組。3&#xff0e;培養學生分析問題、解決問題的能力。二、教學重點、難點和關鍵&#xff08;一&#xff09;重點&#…

為什么從網頁上打印怎們好像被縮放_全網最詳細關于3D打印的zbrush技術

這個小教程旨在為剛開始對3D打印感興趣的新人朋友提供一些快速上手小技巧&#xff0c;以便在zbrush中制作適合桌面FDM 3D打印的創作&#xff0c;本次小教程需要讀者已經有一些zbrush基礎知識。1設置系統單位導入1英尺的box&#xff0c;1英尺X1英尺X1英尺。一旦導入到場景中&…

android圖片瀏覽遠近,快圖瀏覽編輯圖片方法介紹_怎么編輯圖片_3DM手游

既然是圖片管理工具&#xff0c;那么快圖瀏覽肯定也是少不了圖片編輯功能的。如何使用快圖瀏覽編輯圖片呢&#xff1f;下面小編帶來的詳細的使用方法&#xff0c;有需要的小伙伴一起來看看吧&#xff01;編輯照片方法介紹&#xff1a;1.第一步&#xff0c;下載安裝快圖瀏覽&…

h5微信本地調試 vue_vueh5中使用微信sdk

微信官方sdk教程安裝vue 微信sdk 使用NPM安裝&#xff0c;不會的百度學npm install jweixin-module --save創建一個wxsdk.js 文件// 引入微信sdkvar jweixin require(jweixin-module);export default {//判斷是否在微信中isWechat: function() {var ua window.navigator.user…

matlab 三維 作圖 坐標軸_MATLAB學習——MATLAB中的三維繪圖指令

2 基本XYZ立體繪圖命令mesh和plot是三度空間立體繪圖的基本命令&#xff0c;mesh可畫出立體網狀圖&#xff0c;plot則可畫出立體曲面圖&#xff0c;兩者產生的圖形都會依高度而有不同顏色。下列命令可畫出由函數形成的立體網狀圖: xlinspace(-2, 2, 25); % 在x軸上取25點 ylins…

說說python程序的執行過程_表示說的詞語

展開全部1、奉勸 [ fng qun ] 釋義&#xff1a;敬辭。鄭重勸說62616964757a686964616fe58685e5aeb931333365653331。 造句&#xff1a;我們厭惡那些陰險的惡人&#xff0c;奉勸他們改惡為善&#xff0c;才能得到社會的原諒&#xff01; 2、勸告 [ qun go ] 釋義&#xff1a;拿道…

HTML4基本編譯原理,Stanford公開課《編譯原理》學習筆記(1~4課)

課程里涉及到的內容講的還是很清楚的&#xff0c;但個別地方有點脫節&#xff0c;任何看不懂卡住的地方&#xff0c;請自行查閱經典著作《Compilers——priciples, Techniques and Tools》(也就是大名鼎鼎的龍書)的對應章節。一. 編譯的基本流程完整的編譯的5個基本步驟包括lex…

rocketmq 消息指定_SpringBoot 整合 RocketMQ 如何實現消息生產消費?

有時候我們在使用消息隊列的時候&#xff0c;往往需要能夠保證消息的順序消費&#xff0c;而RocketMQ是可以支持消息的順序消費的。RocketMQ在發送消息的時候&#xff0c;是將消息發送到不同的隊列中&#xff0c;然后消費端從多個隊列中讀取消息進行消費&#xff0c;很明顯&…

mysql怎么看實例名_南方“中看不中吃”的前4名水果,蓮霧只是墊底,你怎么看?...

水果很多人都喜歡吃&#xff0c;南方人可以說是最幸福的&#xff0c;因為南方的水果種類有很多種&#xff0c;而且水果的價格也很便宜&#xff0c;一年四季都能吃到便宜又好吃的水果&#xff0c;南方的很多水果&#xff0c;北方人可能都沒有吃過&#xff0c;雖然南方的水果種類…

html頁面怎么加向下滾動,如何使用jQuery向上或向下滾動頁面到錨點?

如何使用jQuery向上或向下滾動頁面到錨點&#xff1f;我正在尋找一種方法來包含幻燈片效果&#xff0c;當您單擊頁面上或下的本地錨點鏈接時。我想要一個你有這樣一個鏈接的東西&#xff1a;link text, img etc.也許添加了一個類&#xff0c;所以你知道你希望這個鏈接是一個滑動…

vuex中的值變化 頁面重新渲染_淺談瀏覽器的渲染過程,重繪與回流

瀏覽器的渲染過程 首先&#xff0c;我們先來了解一下瀏覽器的渲染過程是什么樣的&#xff0c;也就是說瀏覽器把一堆代碼呈現到頁面上的過程是什么樣子的&#xff0c;瀏覽器采用流式布局模型(Flow Bsaed Layout)&#xff0c;根據下圖&#xff0c;我們可以總結出瀏覽器的渲染步驟…

vc 將已有項目打包成dll 并應用于其他項目_.NET混淆器 Dotfuscator使用教程:保護你的應用之存檔報告文件...

Dotfuscator是一款.NET混淆器和壓縮器&#xff0c;防止你的應用程序被反編譯。本篇文章將繼續上一篇文章與大家分享保護應用程序的后續三個部分&#xff1a;存檔報告文件、加強保護和替代方法。存檔報告文件作為構建的一部分&#xff0c;Dotfuscator會生成報告文件(在Dotfuscat…

html文件內容搜索,html讀出文本文件內容

html讀出文本文件內容更新時間&#xff1a;2007年01月22日 00:00:00 作者&#xff1a;Function bytes2BSTR(vIn)strReturn ""For i 1 To LenB(vIn)ThisCharCode AscB(MidB(vIn,i,1))If ThisCharCode strReturn strReturn & Chr(ThisCharCode)ElseNextCharC…

python 定義變量_python-003-變量

1.變量的定義python中,在程序運行時,可以隨著程序的運行更改的量成為變量.簡單理解: 變量就是用來臨時存儲數據的容器.可以認為好比是 逛超市 買面條 使用購物車 裝面條變量 -> 購物車數據 -> 面條2.變量的使用# 第一次輸入一個10 num1 10 # 第二次輸入一個20 num2 20 …

蘋果11是高通基帶嗎_最強對抗!小米11對抗三星、蘋果華為等最高旗艦|喜歡小米嗎?...

哈嘍&#xff0c;您好&#xff01;我是原呵呵&#xff0c;點點關注吧&#xff0c;更多精彩內容等著您小米很快就會展示了2021年的手機&#xff0c;該公司通常會在2月份推出該季節的首個旗艦&#xff0c;但新的小米米11已向前推進了幾個月&#xff0c;并成為了首個采用驍龍888處…