同软件多个线程设置不同ip_多线程--面试知识
一、什么是進程?
軟件需要安裝操作系統之上,軟件安裝之后,需要找到核心的啟動文件來運行軟件。
運行軟件:就是讓軟件執行,其實是將軟件中的程序從硬盤加載到內存中,在內存中開辟當前軟件所需要的內存空間,而負責運行這個軟件的那個內存空間就稱為當前軟件的一個進程。
簡而言之,進程就是正在執行的一個軟件(程序)。
二、什么是線程?
它是程序在執行過程中的一個獨立的運行單元,它是軟件在運行過程中真正干活的地方。
一個軟件運行之后,有一個獨立的進程,這個進程負責整個軟件的生命周期。但是在一個進程中最少有一個線程,它負責具體的功能邏輯。
1、多線程
就是一個進程中運行著多個線程。使用多線程的目的,是為了提高程序的執行效率。
2、多線程執行原理
多線程最終都是需要CPU處理,而CPU(例如單核)真正在某個時間點上它本質上只能處理一個線程,而由于CPU 可以在多個線程之間進行高速切換,導致我們誤認為好像是多個程序同時在執行。
由于現在的電腦的CPU都是多核,導致某個時間點上同時會有多個核心處理不同的線程。
3、是不是線程越多越好?
在合理的范圍內去使用CPU的核心數,是可以提高程序的效率,但是如果無限的開啟線程,會降低CPU的執行效率,可能導致死機。
三、線程和進程的區別?
進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程。進程在執行過程中擁有獨立的內存單元,而多個線程共享內存資源,減少切換次數,從而效率更高。
線程是進程的一個實體,是CPU調度和分派的基本單位,是比程序更小的能獨立運行的基本單位。同一進程中的多個線程之間可以并發執行。
四、創建線程有哪幾種方式?
1、 繼承Thread類創建線程類
- 定義Thread類的子類,并重寫該類的run方法,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱為執行體。
- 創建Thread子類的實例,即創建了線程對象。
- 調用線程對象的start()方法來啟動該線程。
2、通過Runnable接口創建線程類
- 定義runnable接口的實現類,并重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體。
- 創建 Runnable實現類的實例,并依此實例作為Thread的target來創建Thread對象,該Thread對象才是真正的線程對象。
- 調用線程對象的start()方法來啟動該線程。
上面2種方式創建線程都有一個缺陷:就是在執行完任務之后無法獲取執行結果。如果需要獲取執行結果,就必須通過共享變量或者使用線程通信的方式來達到效果,這樣使用起來就比較麻煩。而自從Java 1.5開始,就提供了Callable和Future,通過它們可以在任務執行完畢之后得到任務執行結果。
3、通過Callable和Future創建線程
- 創建Callable接口的實現類,并實現call()方法,該call()方法將作為線程執行體,并且有返回值。
- 創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。
- 使用FutureTask對象作為Thread對象的target創建并啟動新線程。
- 調用FutureTask對象的get()方法來獲得子線程執行結束后的返回值。
- 具體解釋:https://www.cnblogs.com/dolphin0520/p/3949310.html
五、run方法是干什么的?
在Java中給給出Thread類來描述(封裝)操作線程。最終線程是要被開啟執行的,開啟一個線程之后,需要告訴線程應該去干什么事情。而具體需要線程做的事情,Java給出了run方法,需要將具體的線程要執行的代碼編寫在其中。
簡而言之,run方法,就是編寫線程被啟動之后需要執行的代碼的方法。
六、為什么調用要start來啟動線程而不直接去調用run方法?
創建Thread或Thread的子類對象,這時只是代表有線程本身這個對象而已,如果希望程序中能夠出現多個線程同時執行的這種狀態,就必須調用線程自身的 start 方法將線程開啟。
如果直接通過線程對象本身去調用run方法,這時和普通的對象調用方法沒有任何的區別。直接調用run方法,線程根本就沒有開啟,也就是說不會形成多個線程同時執行。
在調用start方法之后,JVM會自動的開辟一個新的線程,然后讓這個新的線程去執行線程中的run方法中的代碼。
七、繼承Thread和實現Runnable有啥區別?
繼承Thread,是讓子類變成線程類??墒窃谀承┣闆r下,類已經有父類,它無法再去繼承Thread的??墒穷愔杏胁糠执a又需要被多線程執行,那么只能讓需要多線程操作的類去實現Runnable接口。
Thread類:
它是描述線程本身的,它提供操作線程的各種方法(start、stop、interrupt等等)。
Runnable接口:
它和線程沒有直接的關系,它其中僅僅提供一個run方法,目的是將需要線程執行的任務書寫在run方法中。最終需要將Runnable接口的實現類的對象交給Thread。
八、線程有哪些狀態?
線程通常都有五種狀態:創建、就緒、運行、阻塞和死亡。
- 創建狀態。在生成線程對象,并沒有調用該對象的start方法,這是線程處于創建狀態。
- 就緒狀態。當調用了線程對象的start方法之后,該線程就進入了就緒狀態,但是此時線程調度程序還沒有把該線程設置為當前線程,此時處于就緒狀態。在線程運行之后,從等待或者睡眠中回來之后,也會處于就緒狀態。
- 運行狀態。線程調度程序將處于就緒狀態的線程設置為當前線程,此時線程就進入了運行狀態,開始運行run函數當中的代碼。
- 阻塞狀態。線程正在運行的時候,被暫停,通常是為了等待某個時間的發生(比如說某項資源就緒)之后再繼續運行。sleep,suspend,wait等方法都可以導致線程阻塞。
- 死亡狀態。如果一個線程的run方法執行結束或者調用stop方法后,該線程就會死亡。對于已經死亡的線程,無法再使用start方法令其進入就緒
九、守護線程是什么?
守護進程(線程)(daemon thread),是個服務線程,它的作用準確地來說就是服務其他的線程。它是一類在后臺運行的特殊進程,用于執行特定的系統任務。很多守護進程在系統引導的時候啟動,并且一直運行直到系統關閉。另一些只在需要的時候才啟動,完成任務后就自動結束。
守護線程又被稱為“服務進程”、“精靈線程”、“后臺線程”,是指在程序運行是在后臺提供一種通用的線程。 通俗點講,任何一個守護線程都是整個JVM中所有非守護線程的“保姆”。
JAVA里線程分2種:
1、守護線程,比如垃圾回收線程,就是最典型的守護線程。
2、用戶線程,就是應用程序里的自定義線程。
十、并行和并發有什么區別?
- 并行是指兩個或者多個事件在同一時刻發生;而并發是指兩個或多個事件在同一時間間隔發生。
- 并行是在不同實體上的多個事件,并發是在同一實體上的多個事件。
- 在一臺處理器上“同時”處理多個任務,在多臺處理器上同時處理多個任務。如hadoop分布式集群。
所以并發編程的目標是充分的利用處理器的每一個核,以達到最高的處理性能。
打個比方:
并發,就像一個人(cpu)喂2個孩子(程序),輪換著每人喂一口,表面上兩個孩子都在吃飯。
并行,就是2個人喂2個孩子,兩個孩子也同時在吃飯。
十一、runnable 和 callable 有什么區別?
- Runnable接口中的run()方法的返回值是void,它做的事情只是純粹地去執行run()方法中的代碼而已;
- Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合可以用來獲取異步執行的結果。
十二、sleep() 和 wait() 有什么區別?
sleep():方法是線程類(Thread)的靜態方法,讓調用線程進入睡眠狀態,讓出執行機會給其他線程,等到休眠時間結束后,線程進入就緒狀態和其他線程一起競爭cpu的執行時間。因為sleep() 是static靜態的方法,它不能改變對象的機鎖,當一個synchronized塊中調用了sleep() 方法,線程雖然進入休眠,但是對象的機鎖沒有被釋放,其他線程依然無法訪問這個對象。
wait():wait()是Object類的方法,當一個線程執行到wait方法時,它就進入到一個和該對象相關的等待池,同時釋放對象的機鎖,使得其他線程能夠訪問,可以通過notify,notifyAll方法來喚醒等待的線程。
十三、notify()和 notifyAll()有什么區別?
1、如果線程調用了對象的 wait()方法,那么線程便會處于該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
2、當有線程調用了對象的 notifyAll ()方法(喚醒所有 wait 線程)或 notify()方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了notify后只有一個線程會由等待池進入鎖池,而notifyAll會將該對象等待池內的所有線程移動到鎖池中,等待鎖競爭。
3、優先級高的線程競爭到對象鎖的概率大,假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調用 wait()方法,它才會重新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。
十四、創建線程池有哪幾種方式?
1、newFixedThreadPool(int nThreads)
創建一個固定長度的線程池,每當提交一個任務就創建一個線程,直到達到線程池的最大數量,這時線程規模將不再變化,當線程發生未預期的錯誤而結束時,線程池會補充一個新的線程。
2、 newCachedThreadPool()
創建一個可緩存的線程池,如果線程池的規模超過了處理需求,將自動回收空閑線程,而當需求增加時,則可以自動添加新線程,線程池的規模不存在任何限制。
3、newSingleThreadExecutor()
這是一個單線程的Executor,它創建單個工作線程來執行任務,如果這個線程異常結束,會創建一個新的來替代它;它的特點是能確保依照任務在隊列中的順序來串行執行。
4、 newScheduledThreadPool(int corePoolSize)
創建了一個固定長度的線程池,而且以延遲或定時的方式來執行任務,類似于Timer。
十五、線程池都有哪些狀態?
線程池有5種狀態:
1、Running
(1) 狀態說明:線程池處在RUNNING狀態時,能夠接收新任務,以及對已添加的任務進行處理。 (2) 狀態切換:線程池的初始化狀態是RUNNING。換句話說,線程池被一旦被創建,就處于RUNNING狀態,并且線程池中的任務數為0!
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));2、ShutDown
(1) 狀態說明:線程池處在SHUTDOWN狀態時,不接收新任務,但能處理已添加的任務。 (2) 狀態切換:調用線程池的shutdown()接口時,線程池由RUNNING -> SHUTDOWN。
3、Stop
(1) 狀態說明:線程池處在STOP狀態時,不接收新任務,不處理已添加的任務,并且會中斷正在處理的任務。 (2) 狀態切換:調用線程池的shutdownNow()接口時,線程池由(RUNNING or SHUTDOWN ) -> STOP。
4、Tidying
(1) 狀態說明:當所有的任務已終止,ctl 記錄的”任務數量”為0,線程池會變為TIDYING狀態。當線程池變為TIDYING狀態時,會執行鉤子函數terminated()。terminated()在ThreadPoolExecutor類中是空的,若用戶想在線程池變為TIDYING時,進行相應的處理;可以通過重載terminated()函數來實現。 (2) 狀態切換:當線程池在SHUTDOWN狀態下,阻塞隊列為空并且線程池中執行的任務也為空時,就會由 SHUTDOWN -> TIDYING。 當線程池在STOP狀態下,線程池中執行的任務為空時,就會由STOP -> TIDYING。
5、Terminated
(1) 狀態說明:線程池徹底終止,就變成TERMINATED狀態。 (2) 狀態切換:線程池處在TIDYING狀態時,執行完terminated()之后,就會由 TIDYING -> TERMINATED。
說明: ctl是一個AtomicInteger類型的原子對象。ctl記錄了"線程池中的任務數量"和"線程池狀態"2個信息。ctl共包括32位。其中,高3位表示"線程池狀態",低29位表示"線程池中的任務數量"。
各個狀態切換框架圖:
十六、線程池的四種拒絕策略
線程池的拒絕策略,是指當任務添加到線程池中被拒絕,而采取的處理措施。當任務添加到線程池中之所以被拒絕,可能是由于:第一,線程池異常關閉。第二,任務數量超過線程池的最大限制。
線程池共包括4種拒絕策略,它們分別是:AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy和DiscardPolicy。
AbortPolicy -- 當任務添加到線程池中被拒絕時,它將拋出 RejectedExecutionException 異常。CallerRunsPolicy -- 當任務添加到線程池中被拒絕時,會在線程池當前正在運行的Thread線程池中處理被拒絕的任務。DiscardOldestPolicy -- 當任務添加到線程池中被拒絕時,線程池會放棄等待隊列中最舊的未處理任務,然后將被拒絕的任務添加到等待隊列中。DiscardPolicy -- 當任務添加到線程池中被拒絕時,線程池將丟棄被拒絕的任務。線程池默認的處理策略是AbortPolicy!
十七、線程池中 submit()和 execute()方法有什么區別?
1、接收的參數不一樣
2、submit有返回值,而execute沒有
3、submit方便Exception處理
十八、在 JAVA 程序中怎么保證多線程的運行安全?
線程安全在三個方面體現:
1、原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操作,(atomic,synchronized);
2、可見性:一個線程對主內存的修改可以及時地被其他線程看到,(synchronized,volatile);
3、有序性:一個線程觀察其他線程中的指令執行順序,由于指令重排序,該觀察結果一般雜亂無序,(happens-before原則)。
十九、多線程鎖的升級原理是什么?
在Java中,鎖共有4種狀態,級別從低到高依次為:無狀態鎖,偏向鎖,輕量級鎖和重量級鎖狀態,這幾個狀態會隨著競爭情況逐漸升級,鎖可以升級但不能降級。
鎖升級的圖示過程:
二十、什么是死鎖?
死鎖是指兩個或兩個以上的進程在執行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。
此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。是操作系統層面的一個錯誤,是進程死鎖的簡稱,最早在 1965 年由 Dijkstra 在研究銀行家算法時提出的,它是計算機操作系統乃至整個并發程序設計領域最難處理的問題之一。
二十一、怎么防止死鎖?
死鎖的四個必要條件:
1、互斥條件:進程對所分配到的資源不允許其他進程進行訪問,若其他進程訪問該資源,只能等待,直至占有該資源的進程使用完成后釋放該資源;
2、請求和保持條件:進程獲得一定的資源之后,又對其他資源發出請求,但是該資源可能被其他進程占有,此事請求阻塞,但又對自己獲得的資源保持不放;
3、不可剝奪條件:是指進程已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用完后自己釋放;
4、環路等待條件:是指進程發生死鎖后,若干進程之間形成一種頭尾相接的循環等待資源關系。
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之 一不滿足,就不會發生死鎖。理解了死鎖的原因,尤其是產生死鎖的四個必要條件,就可以最大可能地避免、預防和解除死鎖。所以,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確定資源的合理分配算法,避免進程永久占據系統資源。
此外,也要防止進程在處于等待狀態的情況下占用資源。因此,對資源的分配要給予合理的規劃。
二十二、ThreadLocal 是什么?有哪些使用場景?
線程局部變量是局限于線程內部的變量,屬于線程自身所有,不在多個線程間共享。Java提供ThreadLocal類來支持線程局部變量,是一種實現線程安全的方式。但是在管理環境下(如 web 服務器)使用線程局部變量的時候要特別小心,在這種情況下,工作線程的生命周期比任何應用變量的生命周期都要長。任何線程局部變量一旦在工作完成后沒有釋放,Java 應用就存在內存泄露的風險。
二十三、簡述 synchronized 底層實現原理?
synchronized 可以保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區,同時它還可以保證共享變量的內存可見性。
Java中每一個對象都可以作為鎖,這是synchronized實現同步的基礎:
- 普通同步方法,鎖是當前實例對象
- 靜態同步方法,鎖是當前類的class對象
- 同步方法塊,鎖是括號里面的對象
二十四、能不能將synchronized 添加在run方法上,為什么?
從語法上講,是可以在run方法上添加同步關鍵字,讓整個run方法同步。但實際是不允許的。 ? 因為run方法是線程要執行的任務方法,也就是線程只有進入到run方法中才能執行任務,而將同步添加在run方法上,意味著線程都無法進入到run方法,更無法執行任務。而線程出run方法,就意味著當前這個線程已經將任務執行完成了。其他線程進入run方法也沒有意義。
二十五、synchronized 和 volatile 的區別是什么?
- volatile本質是在告訴jvm當前變量在寄存器(工作內存)中的值是不確定的,需要從主存中讀取; synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住。
- volatile僅能使用在變量級別;synchronized則可以使用在變量、方法、和類級別的。
- volatile僅能實現變量的修改可見性,不能保證原子性;而synchronized則可以保證變量的修改可見性和原子性。
- volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。
- volatile標記的變量不會被編譯器優化;synchronized標記的變量可以被編譯器優化。
二十五、synchronized 和 Lock 有什么區別?
- 首先synchronized是java內置關鍵字,在jvm層面,Lock是個java類;
- synchronized無法判斷是否獲取鎖的狀態,Lock可以判斷是否獲取到鎖;
- synchronized會自動釋放鎖(a 線程執行完同步代碼會釋放鎖 ;b 線程執行過程中發生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖;
- 用synchronized關鍵字的兩個線程1和線程2,如果當前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會一直等待下去,而Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,線程可以不用一直等待就結束了;
- synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(兩者皆可);
- Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。
二十六、synchronized 和 ReentrantLock 區別是什么?
synchronized是和if、else、for、while一樣的關鍵字,ReentrantLock是類,這是二者的本質區別。既然ReentrantLock是類,那么它就提供了比synchronized更多更靈活的特性,可以被繼承、可以有方法、可以有各種各樣的類變量,ReentrantLock比synchronized的擴展性體現在幾點上:
- ReentrantLock可以對獲取鎖的等待時間進行設置,這樣就避免了死鎖
- ReentrantLock可以獲取各種鎖的信息
- ReentrantLock可以靈活地實現多路通知
- 另外,二者的鎖機制其實也是不一樣的:ReentrantLock底層調用的是Unsafe的park方法加鎖,synchronized操作的應該是對象頭中mark word。
二十七、 簡述 atomic 的原理?
Atomic包中的類基本的特性就是在多線程環境下,當有多個線程同時對單個(包括基本類型及引用類型)變量進行操作時,具有排他性,即當多個線程同時對該變量的值進行更新時,僅有一個線程能成功,而未成功的線程可以向自旋鎖一樣,繼續嘗試,一直等到執行成功。
Atomic系列的類中的核心方法都會調用unsafe類中的幾個本地方法。我們需要先知道一個東西就是Unsafe類,全名為:sun.misc.Unsafe,這個類包含了大量的對C代碼的操作,包括很多直接內存分配以及原子操作的調用,而它之所以標記為非安全的,是告訴你這個里面大量的方法調用都會存在安全隱患,需要小心使用,否則會導致嚴重的后果,例如在通過unsafe分配內存的時候,如果自己指定某些區域可能會導致一些類似C++一樣的指針越界到其他進程的問題
原文:https://www.cnblogs.com/zyouyi/p/11331216.html
鳴謝作者:游戈之淚
總結
以上是生活随笔為你收集整理的同软件多个线程设置不同ip_多线程--面试知识的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python删除文本中指定内容_Pyth
- 下一篇: flutter scrollview_简