導航:首頁 > 編程語言 > java非同步編程筆記

java非同步編程筆記

發布時間:2023-01-24 03:56:10

『壹』 java中同步和非同步有什麼異同

Java中交互方式分為同步和非同步兩種:

相同的地方:

都屬於交互方式,都是發送請求。

不同的地方:

同步交互:指發送一個請求,需要等待返回,然後才能夠發送下一個請求,有個等待過程;

非同步交互:指發送一個請求,不需要等待返回,隨時可以再發送下一個請求,即不需要等待。區別:一個需要等待,一個不需要等待,在部分情況下,我們的項目開發中都會優先選擇不需要等待的非同步交互方式。

(1)java非同步編程筆記擴展閱讀:

Java,是由Sun Microsystems公司於1995年5月推出的Java程序設計語言和Java平台的總稱。用Java實現的HotJava瀏覽器(支持Java applet)顯示了Java的魅力:跨平台、動態的Web、Internet計算。從此,Java被廣泛接受並推動了Web的迅速發展,常用的瀏覽器現均支持Java applet

Java是一種簡單的,面向對象的,分布式的,解釋型的,健壯安全的,結構中立的,可移植的,性能優異、多線程的動態語言。

當1995年SUN推出Java語言之後,全世界的目光都被這個神奇的語言所吸引。那麼Java到底有何神奇之處呢?

Java語言其實最早誕生於1991年,起初被稱為OAK語言,是SUN公司為一些消費性電子產品而設計的一個通用環境。他們最初的目的只是為了開發一種獨立於平台的軟體技術,而且在網路出現之前,OAK可以說是默默無聞,甚至差點夭折。但是,網路的出現改變了OAK的命運。

參考資料:java基礎 網路

『貳』 java 非同步調用方法

asynchronous call(非同步調用)
一個可以無需等待被調用函數的返回值就讓操作繼續進行的方法
中文名
非同步調用
外文名
asynchronous call
領域
函數
傑作
線程


快速
導航
實戰用法非同步調用使用方法
舉例
非同步調用就是你 喊 你朋友吃飯 ,你朋友說知道了 ,待會忙完去找你 ,你就去做別的了。
同步調用就是你 喊 你朋友吃飯 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你們一起去。
實戰用法
操作系統發展到今天已經十分精巧,線程就是其中一個傑作。操作系統把 CPU 處理時間劃分成許多短暫時間片,在時間 T1 執行一個線程的指令,到時間 T2又執行下一線程的指令,各線程輪流執行,結果好象是所有線程在並肩前進。這樣,編程時可以創建多個線程,在同一期間執行,各線程可以「並行」完成不同的任務。
在單線程方式下,計算機是一台嚴格意義上的馮·諾依曼式機器,一段代碼調用另一段代碼時,只能採用同步調用,必須等待這段代碼執行完返回結果後,調用方才能繼續往下執行。有了多線程的支持,可以採用非同步調用,調用方和被調方可以屬於兩個不同的線程,調用方啟動被調方線程後,不等對方返回結果就繼續執行後續代碼。被調方執行完畢後,通過某種手段通知調用方:結果已經出來,請酌情處理。

『叄』 掘術三劍客願代碼是多少

掘術三劍客願代碼是#userIn{margin-left:10px;border:。因為掘術三劍客願代碼是使用c語音和java語音編寫的,所以掘術三劍客願代碼是#userIn{margin-left:10px;border:。

『肆』 Java 非同步編程框架有哪些推薦

前端框架的話EXTJS跟 JQuery 都支持非同步通信,服務端的話JAVA1.7版本或更高版本的並發庫有支持非同步調用的API,若是遠程調用,也就是系統之間調用的話,web service的任何一個框架,比如CXF都支持非同步調用,因為使用的是消息傳遞機制進行通信,若你想用支持遠程過程調用(RPC)的框架的話,那就只能面向多線程編程才能實現非同步調用

『伍』 java 非同步編程

用非同步輸入輸出流編寫Socket進程通信程序
在Merlin中加入了用於實現非同步輸入輸出機制的應用程序介麵包:java.nio(新的輸入輸出包,定義了很多基本類型緩沖(Buffer)),java.nio.channels(通道及選擇器等,用於非同步輸入輸出),java.nio.charset(字元的編碼解碼)。通道(Channel)首先在選擇器(Selector)中注冊自己感興趣的事件,當相應的事件發生時,選擇器便通過選擇鍵(SelectionKey)通知已注冊的通道。然後通道將需要處理的信息,通過緩沖(Buffer)打包,編碼/解碼,完成輸入輸出控制。

通道介紹:
這里主要介紹ServerSocketChannel和 SocketChannel.它們都是可選擇的(selectable)通道,分別可以工作在同步和非同步兩種方式下(注意,這里的可選擇不是指可以選擇兩種工作方式,而是指可以有選擇的注冊自己感興趣的事件)。可以用channel.configureBlocking(Boolean )來設置其工作方式。與以前版本的API相比較,ServerSocketChannel就相當於ServerSocket(ServerSocketChannel封裝了ServerSocket),而SocketChannel就相當於Socket(SocketChannel封裝了Socket)。當通道工作在同步方式時,編程方法與以前的基本相似,這里主要介紹非同步工作方式。

所謂非同步輸入輸出機制,是指在進行輸入輸出處理時,不必等到輸入輸出處理完畢才返回。所以非同步的同義語是非阻塞(None Blocking)。在伺服器端,ServerSocketChannel通過靜態函數open()返回一個實例serverChl。然後該通道調用serverChl.socket().bind()綁定到伺服器某埠,並調用register(Selector sel, SelectionKey.OP_ACCEPT)注冊OP_ACCEPT事件到一個選擇器中(ServerSocketChannel只可以注冊OP_ACCEPT事件)。當有客戶請求連接時,選擇器就會通知該通道有客戶連接請求,就可以進行相應的輸入輸出控制了;在客戶端,clientChl實例注冊自己感興趣的事件後(可以是OP_CONNECT,OP_READ,OP_WRITE的組合),調用clientChl.connect(InetSocketAddress )連接伺服器然後進行相應處理。注意,這里的連接是非同步的,即會立即返回而繼續執行後面的代碼。

選擇器和選擇鍵介紹:
選擇器(Selector)的作用是:將通道感興趣的事件放入隊列中,而不是馬上提交給應用程序,等已注冊的通道自己來請求處理這些事件。換句話說,就是選擇器將會隨時報告已經准備好了的通道,而且是按照先進先出的順序。那麼,選擇器是通過什麼來報告的呢?選擇鍵(SelectionKey)。選擇鍵的作用就是表明哪個通道已經做好了准備,准備干什麼。你也許馬上會想到,那一定是已注冊的通道感興趣的事件。不錯,例如對於伺服器端serverChl來說,可以調用key.isAcceptable()來通知serverChl有客戶端連接請求。相應的函數還有:SelectionKey.isReadable(),SelectionKey.isWritable()。一般的,在一個循環中輪詢感興趣的事件(具體可參照下面的代碼)。如果選擇器中尚無通道已注冊事件發生,調用Selector.select()將阻塞,直到有事件發生為止。另外,可以調用selectNow()或者select(long timeout)。前者立即返回,沒有事件時返回0值;後者等待timeout時間後返回。一個選擇器最多可以同時被63個通道一起注冊使用。
應用實例:
下面是用非同步輸入輸出機制實現的客戶/伺服器實常式序――程序清單1(限於篇幅,只給出了伺服器端實現,讀者可以參照著實現客戶端代碼):

程序類圖

public class NBlockingServer {
int port = 8000;
int BUFFERSIZE = 1024;
Selector selector = null;
ServerSocketChannel serverChannel = null;
HashMap clientChannelMap = null;//用來存放每一個客戶連接對應的套接字和通道

public NBlockingServer( int port ) {
this.clientChannelMap = new HashMap();
this.port = port;
}

public void initialize() throws IOException {
//初始化,分別實例化一個選擇器,一個伺服器端可選擇通道
this.selector = Selector.open();
this.serverChannel = ServerSocketChannel.open();
this.serverChannel.configureBlocking(false);
InetAddress localhost = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(localhost, this.port );
this.serverChannel.socket().bind(isa);//將該套接字綁定到伺服器某一可用埠
}
//結束時釋放資源
public void finalize() throws IOException {
this.serverChannel.close();
this.selector.close();
}
//將讀入位元組緩沖的信息解碼
public String decode( ByteBuffer byteBuffer ) throws
CharacterCodingException {
Charset charset = Charset.forName( "ISO-8859-1" );
CharsetDecoder decoder = charset.newDecoder();
CharBuffer charBuffer = decoder.decode( byteBuffer );
String result = charBuffer.toString();
return result;
}
//監聽埠,當通道准備好時進行相應操作
public void portListening() throws IOException, InterruptedException {
//伺服器端通道注冊OP_ACCEPT事件
SelectionKey acceptKey =this.serverChannel.register( this.selector,
SelectionKey.OP_ACCEPT );
//當有已注冊的事件發生時,select()返回值將大於0
while (acceptKey.selector().select() > 0 ) {
System.out.println("event happened");
//取得所有已經准備好的所有選擇鍵
Set readyKeys = this.selector.selectedKeys();
//使用迭代器對選擇鍵進行輪詢
Iterator i = readyKeys.iterator();
while (i
else if ( key.isReadable() ) {//如果是通道讀准備好事件
System.out.println("Readable");
//取得選擇鍵對應的通道和套接字
SelectableChannel nextReady =
(SelectableChannel) key.channel();
Socket socket = (Socket) key.attachment();
//處理該事件,處理方法已封裝在類ClientChInstance中
this.readFromChannel( socket.getChannel(),
(ClientChInstance)
this.clientChannelMap.get( socket ) );
}
else if ( key.isWritable() ) {//如果是通道寫准備好事件
System.out.println("writeable");
//取得套接字後處理,方法同上
Socket socket = (Socket) key.attachment();
SocketChannel channel = (SocketChannel)
socket.getChannel();
this.writeToChannel( channel,"This is from server!");
}
}
}
}
//對通道的寫操作
public void writeToChannel( SocketChannel channel, String message )
throws IOException {
ByteBuffer buf = ByteBuffer.wrap( message.getBytes() );
int nbytes = channel.write( buf );
}
//對通道的讀操作
public void readFromChannel( SocketChannel channel, ClientChInstance clientInstance )
throws IOException, InterruptedException {
ByteBuffer byteBuffer = ByteBuffer.allocate( BUFFERSIZE );
int nbytes = channel.read( byteBuffer );
byteBuffer.flip();
String result = this.decode( byteBuffer );
//當客戶端發出」@exit」退出命令時,關閉其通道
if ( result.indexOf( "@exit" ) >= 0 ) {
channel.close();
}
else {
clientInstance.append( result.toString() );
//讀入一行完畢,執行相應操作
if ( result.indexOf( "\n" ) >= 0 ){
System.out.println("client input"+result);
clientInstance.execute();
}
}
}
//該類封裝了怎樣對客戶端的通道進行操作,具體實現可以通過重載execute()方法
public class ClientChInstance {
SocketChannel channel;
StringBuffer buffer=new StringBuffer();
public ClientChInstance( SocketChannel channel ) {
this.channel = channel;
}
public void execute() throws IOException {
String message = "This is response after reading from channel!";
writeToChannel( this.channel, message );
buffer = new StringBuffer();
}
//當一行沒有結束時,將當前字竄置於緩沖尾
public void append( String values ) {
buffer.append( values );
}
}

//主程序
public static void main( String[] args ) {
NBlockingServer nbServer = new NBlockingServer(8000);
try {
nbServer.initialize();
} catch ( Exception e ) {
e.printStackTrace();
System.exit( -1 );
}
try {
nbServer.portListening();
}
catch ( Exception e ) {
e.printStackTrace();
}
}
}

程序清單1

小結:
從以上程序段可以看出,伺服器端沒有引入多餘線程就完成了多客戶的客戶/伺服器模式。該程序中使用了回調模式(CALLBACK)。需要注意的是,請不要將原來的輸入輸出包與新加入的輸入輸出包混用,因為出於一些原因的考慮,這兩個包並不兼容。即使用通道時請使用緩沖完成輸入輸出控制。該程序在Windows2000,J2SE1.4下,用telnet測試成功。

『陸』 如何用Java回調和線程實現非同步調用

軟體模塊之間的調用關系可以分為兩大類:即同步調用和非同步調用。在同步調用中,一段代碼(主調方)調用另一段代碼(被調方),主調方必須等待這段代碼執行完成返回結果後,才能繼續往下執行,所以,同步調用是一種阻塞式調用,主調方代碼一直阻塞等待直到被調方返回為止。同步調用相對比較直觀,也是大部分編程語言直接支持的一種調用方式。但是,同步調用在處理比較耗時的情況下會嚴重影響程序性能,影響人機交互的瞬時反應。例如,某個程序需要訪問資料庫獲取大量數據,然後根據這些數據進行一系列處理,將處理結果顯示在程序主窗口。由於資料庫訪問和大量數據的處理都是耗時的工作,在這個工作完成之前,處理結果遲遲不能顯示,用戶點擊滑鼠也不會立即得到響應,讓用戶感到整個程序顯得很沉重。面對這樣一些需要比較長時間才能完成的應用場景,我們需要採用一種非阻塞式調用方式,即非同步調用方式。在非同步調用中,主調方調用被調方後,不等待對方返回結果就繼續執行後續代碼,被調方執行完畢後,通過某種手段通知調用方:結果已經出來,請酌情處理。我們可以對上面的例子改用非同步調用將問題輕松化解:把整個耗時的工作放進一個單獨的線程,由主調方啟動此線程後繼續執行後續代碼,線程在背後悄悄地處理費時的工作,當工作完成,採用回調的方式通知主調方工作完成,主調方將結果顯示在主窗口。經過這樣的處理,主界面繼續進行自己的工作而不必死等,就不會造成界面響應遲鈍。
在實現非同步調用機制時,除了線程之外,還要用到回調。回調是一種雙向調用,也就是,被調方在被調用時也會調用主調方的代碼。在非同步調用中,被調方需要在工作完成時通知主調方,即調用主調方的介面,這一機制通過回調實現。回調和非同步調用的關系非常緊密,回調是非同步調用的基礎[1]。
本文理論聯系實際,首先闡述如何使用Java實現回調機制,然後進一步闡述使用Java回調和線程實現非同步調用,最後,闡述在非同步調用中如何處理超時問題。
1 Java回調機制的實現方法
實現Java回調,需要做如下三件事情:
(1)定義一個回調介面CallbackInterface
介面中聲明回調方法handle,如圖1所示,回調方法就是一個普通的方法,接收一個消息字元串或者一個封裝了數據的事件。
(2)定義一個類實現回調介面
這個類其實就是消息接收者和處理者,也就是調用方,回調方法是消息發生時實際處理消息的方法,此處簡化為一條列印語句。
(3)定義消息通知者
消息通知者也就是被調用方必須具備兩種能力,第一,它必須知道誰是消息接收者,第二,當消息發生時,它能夠回調這些接收者的回調方法。為了獲得這兩種能力,消息通知者首先必須提供一個注冊方法register, 通過注冊的方式來注冊多個對此消息或事件感興趣的對象。然後提供一個消息通知方法notifyMessage,在這個方法中調用所有消息接收者的回調方法。具體代碼如圖3所示。
比如用一個可變數組List用於保存消息接收者,注冊的過程實際上是將消息接收者添加到這個數組,以備在需要通知消息的時候調用這些消息接收者的回調方法。
使用Java回調和線程實現非同步調用
線程是一個獨立的執行流,其本質是程序中一段並發執行的代碼。在非同步調用機制中引入線程,在線程中完成耗時的工作,其目的是讓調用方的主線程繼續執行後續代碼而不需要等待被調方的結果返回。由於不需要等待,這樣我們就等於同時做了兩件事情,而這兩件事情分別是在不同的執行流中執行,主調者在當前的主線程中執行,被調者在另外一個線程中執行,因此提高了程序的效率,避免了界面的響應遲鈍。當被調者執行完成後,仍然採用回調通知主調者。
例如LongTimeWorker是一個用於完成耗時工作的線程,同時又是消息通知者。其耗時工作在run方法中完成,另外提供一個注冊方法register, 和一個消息通知方法notifyMessage,在run方法的最後,即耗時工作完成以後,調用notifyMessage將消息廣播出去。
3 非同步調用中超時問題的處理
非同步調用通常都要加入超時機制,因為我們總是希望在一個指定的時間范圍內返回一個結果,即使沒有得到結果也該有個超時通知。這時我們需要使用「限時線程回調方式」,它在原有線程回調的基礎上加上一個計時器Timer以計算消耗的時間,如果時間期限到了任務還沒有執行完成即中斷線程,並將超時消息廣播出去。LongTimeWorker類需要修改部分的代碼如圖8和圖9所示。
首先LongTimeWorker線程類增加了一個構造方法,其參數是超時時間timeout,構造方法的主要任務是創建一個定時器,每秒鍾計時一次,若超時時間到則終止本線程,並廣播超時消息。LongTimeWorker線程類的第二個改變發生在其run方法中,線程一啟動立即開始計時,完成工作後停止計時,並廣播消息。
4 結束語
非同步調用是一種非阻塞式調用方式,用於在處理比較耗時的任務時保證程序性能不受到影響。實現非同步調用的關鍵在於要解決三個技術難題,它們分別是程序阻塞問題、非同步消息的傳遞問題和超時問題。本文介紹的方法採用並發線程、回調機制和計時器使上述問題得到了圓滿解決。

『柒』 Java非同步編程可以嗎

不論是伺服器端編程還是客戶端編程,編程中的同步和非同步對程序員來說都應該不陌生,我們經常會用同步編程來解決順序執行問題、用非同步解決並行執行問題。然而,就是這樣的常見的編程模式,有人卻將其申請為專利。在谷歌專利查詢網站上專利公開號為US 20140282625 A1的專利內容就是Asynchronous programming model for concurrent workflow scenarios,在這個專利的內容摘要描述有:

非同步方式在編程中的執行過程是:先將包含有跟流程信息相關的全局變數和全局上下文指針存儲到一個上下文結構體中。當非同步函數被執行時,全局上下文指針被存儲到本地變數中,當函數執行完,全局上下文指針可以從本地變數中恢復。

下面的流程圖和組件圖可看出這個專利中含蓋的編程模式和架構。
圖1
圖2
專利是一把雙刃劍,它能保護專利發明人的權利、鼓勵更多人來發明創造,但也會因此限制其推廣和普遍使用、浪費生產力。UNIX 和 BSD 的專利之爭就是最惡劣的一個案例,Java 和安卓之爭也給業界帶來了很多的麻煩。2004 年 4 月,微軟公司申請到一個專利,內容是計算機上,在一個特定時間內按下多次按鍵來啟動應用程序,即微軟為雙擊滑鼠啟動程序申請了專利。幸好微軟沒有動用這項專利,否則的話後果實在太恐怖了。蘋果公司也申請了很多讓人哭笑不得的專利,其中最奇葩的是矩形圓角外觀設計專利。
蘋果此前獲得的諸多關於 iPhone 和 iPad 的專利中或多或少都會包含一些實際的功能,譬如 Home 鍵、背部輪廓設計或者前面板整體玻璃覆蓋設計等。但此次獲得的專利卻單單專注於外觀,蘋果的意圖很簡單:圓角矩形就是蘋果發明的!專利所保護的圓角矩形設計,是一個非常寬泛的概念,寬泛到圖中這個邊框的橫豎線條的比例都能成為它保護的對象,無論設備是 7 寸還是 20 寸。
專利可以用來維護原創者的權益,但是專利的尺度寬泛以及漏洞極有可能通過了一些不實用的專利而引起很多不必要的麻煩,甚至對行業的發展造成阻礙.

『捌』 「高並發」兩種非同步模型與深度解析Future介面-

大家好,我是冰河~~

本文有點長,但是滿滿的干貨,以實際案例的形式分析了兩種非同步模型,並從源碼角度深度解析Future介面和FutureTask類,希望大家踏下心來,打開你的IDE,跟著文章看源碼,相信你一定收獲不小!

在Java的並發編程中,大體上會分為兩種非同步編程模型,一類是直接以非同步的形式來並行運行其他的任務,不需要返回任務的結果數據。一類是以非同步的形式運行其他任務,需要返回結果。

1.無返回結果的非同步模型

無返回結果的非同步任務,可以直接將任務丟進線程或線程池中運行,此時,無法直接獲得任務的執行結果數據,一種方式是可以使用回調方法來獲取任務的運行結果。

具體的方案是:定義一個回調介面,並在介面中定義接收任務結果數據的方法,具體邏輯在回調介面的實現類中完成。將回調介面與任務參數一同放進線程或線程池中運行,任務運行後調用介面方法,執行回調介面實現類中的邏輯來處理結果數據。這里,給出一個簡單的示例供參考。

便於介面的通用型,這里為回調介面定義了泛型。

回調介面的實現類主要用來對任務的返回結果進行相應的業務處理,這里,為了方便演示,只是將結果數據返回。大家需要根據具體的業務場景來做相應的分析和處理。

任務的執行類是具體執行任務的類,實現Runnable介面,在此類中定義一個回調介面類型的成員變數和一個String類型的任務參數(模擬任務的參數),並在構造方法中注入回調介面和任務參數。在run方法中執行任務,任務完成後將任務的結果數據封裝成TaskResult對象,調用回調介面的方法將TaskResult對象傳遞到回調方法中。

到這里,整個大的框架算是完成了,接下來,就是測試看能否獲取到非同步任務的結果了。

在測試類中,使用Thread類創建一個新的線程,並啟動線程運行任務。運行程序最終的介面數據如下所示。

大家可以細細品味下這種獲取非同步結果的方式。這里,只是簡單的使用了Thread類來創建並啟動線程,也可以使用線程池的方式實現。大家可自行實現以線程池的方式通過回調介面獲取非同步結果。

2.有返回結果的非同步模型

盡管使用回調介面能夠獲取非同步任務的結果,但是這種方式使用起來略顯復雜。在JDK中提供了可以直接返回非同步結果的處理方案。最常用的就是使用Future介面或者其實現類FutureTask來接收任務的返回結果。

使用Future介面往往配合線程池來獲取非同步執行結果,如下所示。

運行結果如下所示。

FutureTask類既可以結合Thread類使用也可以結合線程池使用,接下來,就看下這兩種使用方式。

結合Thread類的使用示例如下所示。

運行結果如下所示。

結合線程池的使用示例如下。

運行結果如下所示。

可以看到使用Future介面或者FutureTask類來獲取非同步結果比使用回調介面獲取非同步結果簡單多了。注意:實現非同步的方式很多,這里只是用多線程舉例。

接下來,就深入分析下Future介面。

1.Future介面

Future是JDK1.5新增的非同步編程介面,其源代碼如下所示。

可以看到,在Future介面中,總共定義了5個抽象方法。接下來,就分別介紹下這5個方法的含義。

取消任務的執行,接收一個boolean類型的參數,成功取消任務,則返回true,否則返回false。當任務已經完成,已經結束或者因其他原因不能取消時,方法會返回false,表示任務取消失敗。當任務未啟動調用了此方法,並且結果返回true(取消成功),則當前任務不再運行。如果任務已經啟動,會根據當前傳遞的boolean類型的參數來決定是否中斷當前運行的線程來取消當前運行的任務。

判斷任務在完成之前是否被取消,如果在任務完成之前被取消,則返回true;否則,返回false。

這里需要注意一個細節:只有任務未啟動,或者在完成之前被取消,才會返回true,表示任務已經被成功取消。其他情況都會返回false。

判斷任務是否已經完成,如果任務正常結束、拋出異常退出、被取消,都會返回true,表示任務已經完成。

當任務完成時,直接返回任務的結果數據;當任務未完成時,等待任務完成並返回任務的結果數據。

當任務完成時,直接返回任務的結果數據;當任務未完成時,等待任務完成,並設置了超時等待時間。在超時時間內任務完成,則返回結果;否則,拋出TimeoutException異常。

2.RunnableFuture介面

Future介面有一個重要的子介面,那就是RunnableFuture介面,RunnableFuture介面不但繼承了Future介面,而且繼承了java.lang.Runnable介面,其源代碼如下所示。

這里,問一下,RunnableFuture介面中有幾個抽象方法?想好了再說!哈哈哈。。。

這個介面比較簡單run()方法就是運行任務時調用的方法。

3.FutureTask類

FutureTask類是RunnableFuture介面的一個非常重要的實現類,它實現了RunnableFuture介面、Future介面和Runnable介面的所有方法。FutureTask類的源代碼比較多,這個就不粘貼了,大家自行到java.util.concurrent下查看。

(1)FutureTask類中的變數與常量

在FutureTask類中首先定義了一個狀態變數state,這個變數使用了volatile關鍵字修飾,這里,大家只需要知道volatile關鍵字通過內存屏障和禁止重排序優化來實現線程安全,後續會單獨深度分析volatile關鍵字是如何保證線程安全的。緊接著,定義了幾個任務運行時的狀態常量,如下所示。

其中,代碼注釋中給出了幾個可能的狀態變更流程,如下所示。

接下來,定義了其他幾個成員變數,如下所示。

又看到我們所熟悉的Callable介面了,Callable介面那肯定就是用來調用call()方法執行具體任務了。

看一下WaitNode類的定義,如下所示。

可以看到,WaitNode類是FutureTask類的靜態內部類,類中定義了一個Thread成員變數和指向下一個WaitNode節點的引用。其中通過構造方法將thread變數設置為當前線程。

(2)構造方法

接下來,是FutureTask的兩個構造方法,比較簡單,如下所示。

(3)是否取消與完成方法

繼續向下看源碼,看到一個任務是否取消的方法,和一個任務是否完成的方法,如下所示。

這兩方法中,都是通過判斷任務的狀態來判定任務是否已取消和已完成的。為啥會這樣判斷呢?再次查看FutureTask類中定義的狀態常量發現,其常量的定義是有規律的,並不是隨意定義的。其中,大於或者等於CANCELLED的常量為CANCELLED、INTERRUPTING和INTERRUPTED,這三個狀態均可以表示線程已經被取消。當狀態不等於NEW時,可以表示任務已經完成。

通過這里,大家可以學到一點:以後在編碼過程中,要按照規律來定義自己使用的狀態,尤其是涉及到業務中有頻繁的狀態變更的操作,有規律的狀態可使業務處理變得事半功倍,這也是通過看別人的源碼設計能夠學到的,這里,建議大家還是多看別人寫的優秀的開源框架的源碼。

(4)取消方法

我們繼續向下看源碼,接下來,看到的是cancel(boolean)方法,如下所示。

接下來,拆解cancel(boolean)方法。在cancel(boolean)方法中,首先判斷任務的狀態和CAS的操作結果,如果任務的狀態不等於NEW或者CAS的操作返回false,則直接返回false,表示任務取消失敗。如下所示。

接下來,在try代碼塊中,首先判斷是否可以中斷當前任務所在的線程來取消任務的運行。如果可以中斷當前任務所在的線程,則以一個Thread臨時變數來指向運行任務的線程,當指向的變數不為空時,調用線程對象的interrupt()方法來中斷線程的運行,最後將線程標記為被中斷的狀態。如下所示。

這里,發現變更任務狀態使用的是UNSAFE.putOrderedInt()方法,這個方法是個什麼鬼呢?點進去看一下,如下所示。

可以看到,又是一個本地方法,嘿嘿,這里先不管它,後續文章會詳解這些方法的作用。

接下來,cancel(boolean)方法會進入finally代碼塊,如下所示。

可以看到在finallly代碼塊中調用了finishCompletion()方法,顧名思義,finishCompletion()方法表示結束任務的運行,接下來看看它是如何實現的。點到finishCompletion()方法中看一下,如下所示。

在finishCompletion()方法中,首先定義一個for循環,循環終止因子為waiters為null,在循環中,判斷CAS操作是否成功,如果成功進行if條件中的邏輯。首先,定義一個for自旋循環,在自旋循環體中,喚醒WaitNode堆棧中的線程,使其運行完成。當WaitNode堆棧中的線程運行完成後,通過break退出外層for循環。接下來調用done()方法。done()方法又是個什麼鬼呢?點進去看一下,如下所示。

可以看到,done()方法是一個空的方法體,交由子類來實現具體的業務邏輯。

當我們的具體業務中,需要在取消任務時,執行一些額外的業務邏輯,可以在子類中覆寫done()方法的實現。

(5)get()方法

繼續向下看FutureTask類的代碼,FutureTask類中實現了兩個get()方法,如下所示。

沒參數的get()方法為當任務未運行完成時,會阻塞,直到返回任務結果。有參數的get()方法為當任務未運行完成,並且等待時間超出了超時時間,會TimeoutException異常。

兩個get()方法的主要邏輯差不多,一個沒有超時設置,一個有超時設置,這里說一下主要邏輯。判斷任務的當前狀態是否小於或者等於COMPLETING,也就是說,任務是NEW狀態或者COMPLETING,調用awaitDone()方法,看下awaitDone()方法的實現,如下所示。

接下來,拆解awaitDone()方法。在awaitDone()方法中,最重要的就是for自旋循環,在循環中首先判斷當前線程是否被中斷,如果已經被中斷,則調用removeWaiter()將當前線程從堆棧中移除,並且拋出InterruptedException異常,如下所示。

接下來,判斷任務的當前狀態是否完成,如果完成,並且堆棧句柄不為空,則將堆棧中的當前線程設置為空,返回當前任務的狀態,如下所示。

當任務的狀態為COMPLETING時,使當前線程讓出CPU資源,如下所示。

如果堆棧為空,則創建堆棧對象,如下所示。

如果queued變數為false,通過CAS操作為queued賦值,如果awaitDone()方法傳遞的timed參數為true,則計算超時時間,當時間已超時,則在堆棧中移除當前線程並返回任務狀態,如下所示。如果未超時,則重置超時時間,如下所示。

如果不滿足上述的所有條件,則將當前線程設置為等待狀態,如下所示。

接下來,回到get()方法中,當awaitDone()方法返回結果,或者任務的狀態不滿足條件時,都會調用report()方法,並將當前任務的狀態傳遞到report()方法中,並返回結果,如下所示。

看來,這里還要看下report()方法啊,點進去看下report()方法的實現,如下所示。

可以看到,report()方法的實現比較簡單,首先,將outcome數據賦值給x變數,接下來,主要是判斷接收到的任務狀態,如果狀態為NORMAL,則將x強轉為泛型類型返回;當任務的狀態大於或者等於CANCELLED,也就是任務已經取消,則拋出CancellationException異常,其他情況則拋出ExecutionException異常。

至此,get()方法分析完成。注意:一定要理解get()方法的實現,因為get()方法是我們使用Future介面和FutureTask類時,使用的比較頻繁的一個方法。

(6)set()方法與setException()方法

繼續看FutureTask類的代碼,接下來看到的是set()方法與setException()方法,如下所示。

通過源碼可以看出,set()方法與setException()方法整體邏輯幾乎一樣,只是在設置任務狀態時一個將狀態設置為NORMAL,一個將狀態設置為EXCEPTIONAL。

至於finishCompletion()方法,前面已經分析過。

(7)run()方法與runAndReset()方法

接下來,就是run()方法了,run()方法的源代碼如下所示。

可以這么說,只要使用了Future和FutureTask,就必然會調用run()方法來運行任務,掌握run()方法的流程是非常有必要的。在run()方法中,如果當前狀態不是NEW,或者CAS操作返回的結果為false,則直接返回,不再執行後續邏輯,如下所示。

接下來,在try代碼塊中,將成員變數callable賦值給一個臨時變數c,判斷臨時變數不等於null,並且任務狀態為NEW,則調用Callable介面的call()方法,並接收結果數據。並將ran變數設置為true。當程序拋出異常時,將接收結果的變數設置為null,ran變數設置為false,並且調用setException()方法將任務的狀態設置為EXCEPTIONA。接下來,如果ran變數為true,則調用set()方法,如下所示。

接下來,程序會進入finally代碼塊中,如下所示。

這里,將runner設置為null,如果任務的當前狀態大於或者等於INTERRUPTING,也就是線程被中斷了。則調用()方法,接下來,看下()方法的實現。

可以看到,()方法的實現比較簡單,當任務的狀態為INTERRUPTING時,使用while()循環,條件為當前任務狀態為INTERRUPTING,將當前線程佔用的CPU資源釋放,也就是說,當任務運行完成後,釋放線程所佔用的資源。

runAndReset()方法的邏輯與run()差不多,只是runAndReset()方法會在finally代碼塊中將任務狀態重置為NEW。runAndReset()方法的源代碼如下所示,就不重復說明了。

(8)removeWaiter()方法

removeWaiter()方法中主要是使用自旋循環的方式來移除WaitNode中的線程,比較簡單,如下所示。

最後,在FutureTask類的最後,有如下代碼。

關於這些代碼的作用,會在後續深度解析CAS文章中詳細說明,這里就不再探討。

至此,關於Future介面和FutureTask類的源碼就分析完了。

好了,今天就到這兒吧,我是冰河,我們下期見~~

『玖』 java非同步方法什麼意思

在JAVA平台,實現非同步調用的角色有如下三個角色:調用者,取貨憑證,真實數據 非同步調用就是:一個調用者在調用耗時操作,不能立即返回數據時,先返回一個取貨憑證.然後在過一斷時間後憑取貨憑證來獲取真正的數據.
如果數據將在線程間共享。例如正在寫的數據以後可能被另一個線程讀到,或者正在讀的數據可能已經被另一個線程寫過了,那麼這些數據就是共享數據,必須進行同步存取。當應用程序在對象上調用了一個需要花費很長時間來執行的方法,並且不希望讓程序等待方法的返回時,就應該使用非同步編程,在很多情況下採用非同步途徑往往更有效率 只有一個馬桶 很多人上廁所 要排隊 這叫同步迅雷一次可以下載很多東西 這叫非同步

『拾』 java同步和非同步的區別

java同步和非同步的區別如下:

一、根據情況需要專門的線程方式

如果數據將在線程間共享.例如正在寫的數據以後可能被另一個線程讀到,或者正在讀的數據可能已經被另一個線程寫過了,那麼這些數據就是共享數據,必須進行同步存取.

當應用程序在對象上調用了一個需要花費很長時間來執行的方法,並且不希望讓程序等待方法的返回時,就應該使用非同步編程,在很多情況下採用非同步途徑往往更有效率.

二、應用不同:

(說明:以下有些例子為了突出重點,省略了不必要的代碼.非凡是省掉了一些成員變數,就是需要同步的對象.)

1. 多線程中對共享、可變的數據進行同步.

對於函數中的局部變數沒必要進行同步.

對於不可變數據,也沒必要進行同步.

多線程中訪問共享可變數據才有必要.

2. 單個線程中可以使用synchronized,而且可以嵌套,但無意義.

class Test {

public static void main(String[] args) {

Test t = new Test();

synchronized(t) {

synchronized(t) {

System.out.println("ok!");

}

}

}

}

3. 對象實例的鎖

class Test{

public synchronized void f1(){

//do something here

}

public void f2(){

synchronized(this){

//do something here

}

}

}

上面的f1()和f2()效果一致, synchronized取得的鎖都是Test某個實列(this)的鎖.

比如: Test t = new Test();

線程A調用t.f2()時, 線程B無法進入t.f1(),直到t.f2()結束.

作用: 多線程中訪問Test的同一個實例的同步方法時會進行同步.

4. class的鎖

class Test{

final static Object o= new Object();

public static synchronized void f1(){

//do something here

}

public static void f2(){

synchronized(Test.class){

//do something here

}

}

public static void f3(){

try {

synchronized (Class.forName("Test")) {

//do something here

}

}

catch (ClassNotFoundException ex) {

}

}

public static void g(){

synchronized(o){

//do something here

}

}

}

上面f1(),f2(),f3(),g()效果一致

f1(),f2(),f3()中synchronized取得的鎖都是Test.class的鎖.

g()是自己產生一個對象o,利用o的鎖做同步

作用: 多線程中訪問此類或此類任一個實例的同步方法時都會同步. singleton模式lazily initializing屬於此類.

5. static method

class Test{

private static int v = 0;

public static void f1(){

//do something, 但函數中沒用用到v

}

public synchronized static void f2(){

//do something, 函數中對v進行了讀/寫.

}

}

多線程中使用Test的某個實列時,

(1) f1()是線程安全的,不需要同步

(2) f2()這個靜態方法中使用了函數外靜態變數,所以需要同步.

閱讀全文

與java非同步編程筆記相關的資料

熱點內容
鬼龕 又名 鬼屋 瀏覽:395
java截取中文字元 瀏覽:278
{"dc":"zdha26"} 瀏覽:814
張岱pdf 瀏覽:866
51單片機和快閃記憶體晶元的區別 瀏覽:314
韓國下葯強奸電影 瀏覽:238
男同裸體游泳電影 瀏覽:331
一個小男孩和一個小女孩在餐廳吃飯電影 瀏覽:362
pc版微信如何設置代理伺服器 瀏覽:295
毒app怎麼中簽幾率 瀏覽:262
鄰居的姐妹們 瀏覽:380
哚哚電視劇最好看電視劇 瀏覽:407
美國在泰國拍的一部鱷魚電影 瀏覽:862
百合小說大全txt 瀏覽:407
Nam-hyeok 瀏覽:192
新世界第一早拽 瀏覽:70
徐錦江跟葉子楣拍的電影叫什麼名字 瀏覽:636
法國兩個女生吹豎笛是什麼電影 瀏覽:569
頭條用什麼品牌伺服器 瀏覽:90
什麼app查車的維修記錄 瀏覽:243