JAVA中線程中的異步和同步

今天看到AJAX(異步加載)感覺怪怪的!!!
怪就怪在 異步 兩個字上!

問題

、異步和多線程有什麼區別?
其實,異步是目的,而多線程是實現這個目的的方法。異步是說,A發起一個操作後(一般都是比較耗時的操作,如果不耗時的操作就沒有必要異步了),B可以繼續自顧自的處理它自己的事兒,不用幹等着這個耗時操作返回。
、隨着擁有多個硬線程CPU(超線程、雙核)的普及,多線程和異步操作等併發程序設計方法也受到了更多的關注和討論。

多線程和異步操作的異同

多線程和異步操作兩者都可以達到避免調用線程阻塞的目的,從而提高軟件的可響應性。甚至有些時候我們就認爲多線程和異步操作是等同的概念。但是,多線程和異步操作還是有一些區別的。而這些區別造成了使用多線程和異步操作的時機的區別。

異步操作的本質

所有的程序最終都會由計算機硬件來執行。

線程的本質

線程不是一個計算機硬件的功能,而是操作系統提供的一種邏輯功能,線程本質上是進程中一段併發運行的代碼,所以線程需要操作系統投入CPU資源來運行和調度。

異步操作的優缺點

因爲異步操作無須額外的線程負擔,並且使用回調的方式進行處理,在設計良好的情況下,處理函數可以不必使用共享變量(即使無法完全不用,最起碼可以減少 共享變量的數量),減少了死鎖的可能。當然異步操作也並非完美無暇。編寫異步操作的複雜程度較高,程序主要使用回調方式進行處理,與普通人的思維方式有些差異,而且難以調試。

多線程的優缺點

多線程的優點很明顯,線程中的處理程序依然是順序執行,符合普通人的思維習慣,所以編程簡單。但是多線程的缺點也同樣明顯,線程的使用(濫用)會給系統帶來上下文切換的額外負擔。並且線程間的共享變量可能造成死鎖的出現。

而線程的適用範圍則是那種需要長時間CPU運算的場合,例如耗時較長的圖形處理和算法執行。但是往 往由於使用線程編程的簡單和符合習慣,所以很多朋友往往會使用線程來執行耗時較長的I/O操作。這樣在只有少數幾個併發操作的時候還無傷大雅,如果需要處 理大量的併發操作時就不合適了。

異步調用與多線程

異步調用並不是要減少線程的開銷, 它的主要目的是讓調用方法的主線程不需要同步等待在這個函數調用上, 從而可以讓主線程繼續執行它下面的代碼.與此同時, 系統會通過從ThreadPool中取一個線程來執行,幫助我們將我們要寫/讀的數據發送到網卡.由於不需要我們等待, 我們等於同時做了兩件事情. 這個效果跟自己另外啓動一個線程來執行等待方式的寫操作是一樣的.但是, 異步線程可以利用操作系統/.Net的線程池, 系統可以根據吞吐量動態的管理線程池的大小.

異步與多線程:

從辯證關係上來看,異步和多線程並不時一個同等關係,異步是目的,多線程只是我們實現異步的一個手段.

什麼是異步:

異步是當一個調用請求發送給被調用者,而調用者不用等待其結果的返回.實現異步可以採用多線程技術或則交給另外的進程來處理

線程池的實現方法與線程是不一樣的.初始化時在線程池裏的線程爲0.當進程需要一個線程時,創建一個線程,由此線程執行用戶的方法.需要注意的是,此線程執行完後並不立即銷燬,而是掛起等待,如果有其他方法需要執行,回喚醒進行處理.只有當它等到幾秒還沒有任務執行時才喚醒自己,並銷燬自己,釋放資源.當然,如果線程池中的線程不夠處理任務時,會再次創建一個新線程進行執行.

異步有的時候用普通的線程,有的時候用系統的異步調用功能。有一些IO操作也是異步的,但是未必需要一個線程來運行。例如:硬件是有DMA功能的,在調用DMA傳輸數據的時候,CPU是不需要執行處理的,只需要發起傳輸和等待傳輸結束即可。具體到.net平臺,比如Socket的BeginSend,如果是運行在Windows 2000以後的平臺,在底層就會調用異步的完成端口來發送。

異步執行也得執行:

不在當前線程執行,當然得去另外一個線程執行。異步通常用系統線程池的線程,通常情況下性能好些。(因爲可以多次利用,申請時不需要重新申請一個線程,只需要從池裏取就行了。)異步是一種效果,多線程是一種具體技術。可以說,用“多線程”實現“異步”。

異步和多線程是兩個不同的概念:

不能這樣比較.異步請求一般用在IO等耗時操作上,他的好處是函數調用立即返回,相應的工作線程立即返還給系統以供重用。由於系統的線程資源是非常寶貴的,通常有一定的數目限制,而如果用同步方式,那麼每個請求都自始至終佔用這一個線程,服務器可以同時服務的請求數就少了。當異步操作執行完成後,系統會從可用線程中選取一個執行回調程序,這時的這個線程可能是剛開始發出請求的那個線程,也可能是其他的線程,因爲系統選取線程是隨機的事情,所以不能說絕對不是剛開始的那個線程。多線程是用來併發的執行多個任務。

不過有個問題,異步有時優先級比主線程還高。這個特點和多線程不同。

使用場景:

進本在於一個無關緊要的, 耗時的****漫長查詢中,因爲耗時原因用戶請求進來不可能把時間浪費在過長時間來加載你無關緊要的數據中。 或者是文件的上傳, 處理中使用, 一個視頻幾百兆上傳一次要幾分鐘, 你不可能讓客戶等待過長的時間, 而是在開啓異步線程後用戶可以處理其他操作, 等待你異步上傳視頻處理完成後, 推送一個處理完成的提示即可。可以使用MQ來完成這些操作

也可以使用線程池開啓異步方式:

	//定義一個線程池
	ExecutorService threadPool = Executors.newFixedThreadPool(10);
	//開啓異步任務,定義對應的返回類型,提交給對應的線程池
	CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
		return 10;
	},threadPool)
		 .thenApply((r)->{
		 	//接收上次結果在加工
			return 10 + 1;
         })
		.whenComplete((r,e)->{
			  System.out.println("方法完成後執行的結果是:"+r);
              System.out.println("方法完成後執行的異常是:"+e);
		});

執行結果:

	方法完成後執行的結果是:11
	方法完成後執行的異常是:null

綜上所述我們在項目開發時, 某些地方還是需要足夠快的響應時間, 來優化用戶體驗。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章