多執行緒學習(扣丁學堂)-----阿冬專欄!!!
多執行緒學習
補充:併發和並行的不同。
如果你的計算機只有一個處理器,無論是多執行緒還是多程序,在某一時刻,都是隻有一個執行緒/程序在執行,作業系統會讓這些執行緒輪流執行,從巨集觀上看是併發,但是不是並行。
如果你的計算機有多個處理器(包括多核),那麼每個處理器可以同時執行一個執行緒,可能這些執行緒來自一個程序,也可能來自多個程序。此時叫做並行。
如果你的伺服器只有一個程序,並且其中只有一個執行緒,當然不可能併發,但是不等於說,不能實現一個這樣的單執行緒的伺服器同時處理多個客戶端的訪問要求。因為計算機處理請求的時間是非常短的,即便是單執行緒,從巨集觀上看,也足夠滿足多個客戶端輪流請求伺服器的需要。
如果我們追溯最早最早,或者最原始最原始的作業系統,這種粗陋的作業系統可以不區分執行緒和程序。從這個意義去理解,程序和執行緒有很多相通的地方,甚至本質山它們是同源的。等到作業系統複雜到一定程度,我們有了兩種不同的需求,一種是為了程式和程式之間減少干擾,一個程式崩潰,系統別的程式不受影響,這樣我們有了程序,程序強調隔離性。程序和程序有各自獨立的儲存。另一種是方便程式和程式之間互相通訊,同時為了提高效能減小程式切換的開銷,因此我們有了執行緒,執行緒除了堆疊以外,共享全部的儲存。所以也可以說,程序就是重型的執行緒,也可以說執行緒是輕量的程序。
2\(1)理論上說,執行緒是併發執行(並且在多處理器計算機上並行執行),但是還取決於這麼幾個條件:
(2)我說了,程序相當於重量級的執行緒,因此多程序類似多執行緒,區別只是多程序無法共享記憶體(如果需要共享記憶體,相當於程序的通訊)。另外在效能上,程序的啟動、執行、切換的開銷要執行緒大很多。這麼說,執行緒切換可能只要幾十個幾百個時鐘,納秒級別,而程序的切換需要上萬始終,毫秒級別。一個計算機可以有幾千幾萬的執行緒,但是這麼多的程序恐怕就吃不消了。
3\
(1) 多執行緒的併發,是針對單個程序而言的。 一個程序下,可以建立多個執行緒,共享資料,平行計算。
(2)關於售票系統,只要是有介面的,就必然有多執行緒。你點選了某個按鈕,系統會建立子執行緒去查詢資料庫,處理請求;同時你點選其他按鈕,也會立即響應。
就算只做一件事,也是可以並行操作的。 一件事可以拆分成很多步驟的。
(3)開啟兩個瀏覽器買票這種,多程序是瀏覽器實現的,程式碼在瀏覽器中執行;伺服器只負責接收請求,處理請求並返回結果,應該會用到多執行緒。
(4)多程序併發和多執行緒併發只是針對不同的問題,提供的一種解決方案。
一般情況下,一個軟體是單程序執行的,內部多執行緒處理; B/S模式,通過瀏覽器訪問網頁,這個確實是多程序的併發,相互之間,不會影響。
就算是多個程序執行,同一個程序下,也是會有多個執行緒的。
4\
java 關於類似售票系統的多執行緒併發 的問題
本人對多程序和多執行緒不大瞭解,還請各位高手解答下,先謝了。問題如下:
1.多執行緒的併發,我查了下資料,一般解釋的是:一個程序可以包含多個執行緒,一個執行緒完成一個功能,這樣,程序執行的時候,它包含的多個執行緒可以看成同時執行。
那多執行緒的併發,可不可以有另外的理解:如果這時的某個程序只有一個執行緒,那麼,同時有多個類似的程序同時啟動,這時是不是就產生了多個程序下要同時完成相同任務的多個執行緒,這算不算多執行緒的併發?
下面拿售票系統舉例說明下,
2.像售票系統,應該就幹了一件事,就是售票(包括查詢),java實現的時候是不是就是一個執行緒?即一個程序對應一個執行緒。
3.這時,同一個人同一臺電腦上,開啟A瀏覽器買票,然後又開啟B瀏覽器買票,是不是相當於有2個程序(不是執行緒)?
4.那售票系統的多執行緒併發 ,可不可以理解為多程序的併發?
答:問題一: 程序和執行緒是兩個不同的概念, 一個是作業系統級, 一個是應用程式開發級, 兩者都有併發的問題, 但程序的併發一般不是你能解決得好的.
問題二:售票系統看你怎麼開發了,如果是B/S+資料庫模式, 你自己要在程式中考慮併發問題, 主要是對資料庫的併發.
問題三:系統會產生兩個子程序.這是由瀏覽器父程序分配產生的.
問題四:這個問題提得不好,不要糾結概念了, 自己找書看看售票系統的開發例子吧,很多的.B/S模式和C/S模式是不同的.現在一般用B/S模式你只考慮資料庫的併發了,感覺你想得太多,動手太少.,
1、 程序與執行緒、執行緒講解
2、執行緒的操作方法
守護執行緒:???不等於linux作業系統的守護程序,也就是通常說的Daemon程序,是Linux中的後臺服務程序。它是一個生存期較長的程序,通常獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。守護程序常常在系統引導裝入時啟動,在系統關閉時終止。Linux系統有很多守護程序,大多數服務都是通過守護程序實現的,同時,守護程序還能完成許多系統任務,例如,作業規劃程序crond、列印程序lqd等(這裡的結尾字母d就是Daemon的意思)。
由於在Linux中,每一個系統與使用者進行交流的介面稱為終端,每一個從此終端開始執行的程序都會依附於這個終端,這個終端就稱為這些程序的控制終端,當控制終端被關閉時,相應的程序都會自動關閉。但是守護程序卻能夠突破這種限制,它從被執行開始運轉,直到整個系統關閉時才退出。如果想讓某個程序不因為使用者或終端或其他地變化而受到影響,那麼就必須把這個程序變成一個守護程序。
守護執行緒:???
守護執行緒(Daemon)
Java有兩種Thread:“守護執行緒Daemon”與“使用者執行緒User”。
我們之前看到的例子都是使用者,守護執行緒是一種“在後臺提供通用性支援”的執行緒,它並不屬於程式本體。
從字面上我們很容易將守護執行緒理解成是由虛擬機器(virtual machine)在內部建立的,而使用者執行緒則是自己所建立的。事實並不是這樣,任何執行緒都可以是“守護執行緒Daemon”或“使用者執行緒User”。他們在幾乎每個方面都是相同的,唯一的區別是判斷虛擬機器何時離開:
使用者執行緒:Java虛擬機器在它所有非守護執行緒已經離開後自動離開。
守護執行緒:守護執行緒則是用來服務使用者執行緒的,如果沒有其他使用者執行緒在執行,那麼就沒有可服務物件,也就沒有理由繼續下去。
setDaemon(boolean on)方法可以方便的設定執行緒的Daemon模式,true為Daemon模式,false為User模式。setDaemon(boolean on)方法必須線上程啟動之前呼叫,當執行緒正在執行時呼叫會產生異常。isDaemon方法將測試該執行緒是否為守護執行緒。值得一提的是,當你在一個守護執行緒中產生了其他執行緒,那麼這些新產生的執行緒不用設定Daemon屬性,都將是守護執行緒,使用者執行緒同樣。
例:我們所熟悉的Java垃圾回收執行緒就是一個典型的守護執行緒,當我們的程式中不再有任何執行中的Thread,程式就不會再產生垃圾,垃圾回收器也就無事可做,所以當垃圾回收執行緒是Java虛擬機器上僅剩的執行緒時,Java虛擬機器會自動離開。
import java.io.IOException;
/**
* 守護執行緒在沒有使用者執行緒可服務時自動離開
*/
public class TestMain4 extends Thread {
public TestMain4() {
}
/**
* 執行緒的run方法,它將和其他執行緒同時執行
*/
public void run() {
for(int i = 1; i <=100; i++){
try {
Thread.sleep(100);
} catch(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(i);
}
}
public static voidmain(String [] args){
TestMain4 test = newTestMain4();
test.setDaemon(true);
test.start();
System.out.println("isDaemon = " + test.isDaemon());
try {
System.in.read();
// 接受輸入,使程式在此停頓,一旦接收到使用者輸入,main執行緒結束,守護執行緒自動結束
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
最近重新研究Java基礎知識,發現以前太多知識知識略略帶過了,比較說Java的執行緒機制,在Java中有兩類執行緒:User Thread(使用者執行緒)、Daemon Thread(守護執行緒) ,(PS:以前忽略了)。
估計學過Unix開發但是沒有細緻學習Java的同學們會疑惑了,作業系統裡面是沒有所謂的守護執行緒的概念,只有守護程序一說,但是Java語言機制是構建在JVM的基礎之上的,意思是Java平臺把作業系統的底層給遮蔽起來,所以它可以在它自己的虛擬的平臺裡面構造出對自己有利的機制,而語言或者說平臺的設計者多多少少是收到Unix思想的影響,而守護執行緒機制又是對JVM這樣的平臺湊合,於是守護執行緒應運而生。
Daemon的作用是為其他執行緒的執行提供服務,比如說GC執行緒。其實User Thread執行緒和Daemon Thread守護執行緒本質上來說去沒啥區別的,唯一的區別之處就在虛擬機器的離開:如果UserThread全部撤離,那麼DaemonThread也就沒啥執行緒好服務的了,所以虛擬機器也就退出了。
守護執行緒並非虛擬機器內部可以提供,使用者也可以自行的設定守護執行緒,方法:public final voidsetDaemon(boolean on) ;但是有幾點需要注意:
1)、thread.setDaemon(true)必須在thread.start()之前設定,否則會跑出一個IllegalThreadStateException異常。你不能把正在執行的常規執行緒設定為守護執行緒。 (備註:這點與守護程序有著明顯的區別,守護程序是建立後,讓程序擺脫原會話的控制+讓程序擺脫原程序組的控制+讓程序擺脫原控制終端的控制;所以說寄託於虛擬機器的語言機制跟系統級語言有著本質上面的區別)
2)、 在Daemon執行緒中產生的新執行緒也是Daemon的。 (這一點又是有著本質的區別了:守護程序fork()出來的子程序不再是守護程序,儘管它把父程序的程序相關資訊複製過去了,但是子程序的程序的父程序不是init程序,所謂的守護程序本質上說就是“父程序掛掉,init收養,然後檔案0,1,2都是/dev/null,當前目錄到/”)
3)、不是所有的應用都可以分配給Daemon執行緒來進行服務,比如讀寫操作或者計算邏輯。因為在DaemonThread還沒來的及進行操作時,虛擬機器可能已經退出了。
例子:
//完成檔案輸出的守護執行緒任務
importjava.io.*;
class TestRunnableimplements Runnable{
public void run(){
try{
Thread.sleep(1000);//守護執行緒阻塞1秒後執行
File f=new File("daemon.txt");
FileOutputStream os=new FileOutputStream(f,true);
os.write("daemon".getBytes());
}
catch(IOException e1){
e1.printStackTrace();
}
catch(InterruptedException e2){
e2.printStackTrace();
}
}
}
public classTestDemo2{
public static void main(String[] args) throws InterruptedException
{
Runnable tr=new TestRunnable();
Thread thread=new Thread(tr);
thread.setDaemon(true); //設定守護執行緒
thread.start(); //開始執行分程序
}
}
執行結果:檔案daemon.txt中沒有"daemon"字串。
但是如果把thread.setDaemon(true); //設定守護執行緒註釋掉,檔案daemon.txt是可以被寫入daemon字串的
JRE判斷程式是否執行結束的標準是所有的前臺執執行緒行完畢了,而不管後臺執行緒的狀態,因此,在使用後臺執行緒候一定要注意這個問題。
但是daemonThread實際應用在那裡呢?舉個例子,web伺服器中的Servlet,容器啟動時後臺初始化一個服務執行緒,即排程執行緒,負責處理http請求,然後每個請求過來排程執行緒從執行緒池中取出一個工作者執行緒來處理該請求,從而實現併發控制的目的。
網上摘的一個圖,方便大家理解:
Yield:????? 放棄,屈服; 生利; 退讓,退位;
1、同步程式碼塊(會犧牲一定的效能來獲取安全,同步程式碼不宜太長)
2、 同步方法
3、 Lock(1.5以後加入的新特性)
執行緒安全問題:當很多的執行緒一起要共享一個數據的時候,大家去搶就會出現問題,所以就要排隊,排隊的時候就需要上鎖,誰用誰碩上,他用完了別人才能用,這就是同步(排隊)
多個執行緒共享一個數據的時候,這種排隊(同步),就會使得執行緒安全。這種安全是對資料來說的,當然,執行緒安全帶來的代價就是效能降低,
分析一個具體的例子:12306買票問題或者售票執行緒問題?
(涉及到併發\同步\等等)
??????
互不謙讓,互相拿著對方
死鎖的最大原因:你在你當前的同步方法裡面呼叫了其他物件的同步方法,你比較危險
中斷執行緒
注意: 讓執行緒停止得讓他自己自然終止,而不是說讓他卡直接去終止,因為他正在捉一件事情,即使你讓他停止,他有可能停止也會做一些額外的事情的。(執行緒的停止我們要讓執行緒的內部自己去停止,通常是通過一個標記的方式來設定他停止)。所以上面的stop()方法不能用,API文件裡面已提示,此處用的自定義標記來中斷執行緒。
舉個形象的例子:高速公路上的客車中有個小孩突然想小便,他說:要小便,司機不可能立馬停車,需要找個合適的地方停靠一下,……即,司機得自己處以一下停車的時間
後續更新………………繼續學習中!!!!