java 同步 非同步

來源:互聯網
上載者:User

所謂非同步輸入輸出機制,是指在進行輸入輸出處理時,不必等到輸入輸出處理完畢才返回。所以非同步同義語是非阻塞(None Blocking)。

 

網上有很多網友用很通俗的比喻  把同步和非同步講解的很透徹 轉過來

 

舉個例子:普通B/S模式(同步)AJAX技術(非同步) 
          同步:提交請求->等待伺服器處理->處理完畢返回 這個期間用戶端瀏覽器不能幹任何事 
          非同步:  請求通過事件觸發->伺服器處理(這是瀏覽器仍然可以作其他事情)->處理完畢

 

同步就是你叫我去吃飯,我聽到了就和你去吃飯;如果沒有聽到,你就不停的叫,直到我告訴你聽到了,才一起去吃飯。 
非同步就是你叫我,然後自己去吃飯,我得到訊息後可能立即走,也可能等到下班才去吃飯。 
所以,要我請你吃飯就用同步的方法,要請我吃飯就用非同步方法,這樣你可以省錢。

 

 

以通訊為例 
          同步:發送一個請求,等待返回,然後再發送下一個請求 
          非同步:發送一個請求,不等待返回,隨時可以再發送下一個請求 
          並發:同時發送多個請求

 

 

 

下面再轉一段關於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個通道一起註冊使用。

 

           應用執行個體:

    下面是用非同步輸入輸出機制實現的客戶/伺服器執行個體程式�D�D程式清單1(限於篇幅,只給出了伺服器端實現,讀者可以參照著實現用戶端代碼):

  1. public class NBlockingServer {
  2.     int port = 8000;
  3.     int BUFFERSIZE = 1024;
  4.     Selector selector = null;
  5.     ServerSocketChannel serverChannel = null;
  6.     HashMap clientChannelMap = null;//用來存放每一個客戶串連對應的通訊端和通道
  7.     public NBlockingServer( int port ) {
  8.         this.clientChannelMap = new HashMap();
  9.         this.port = port;
  10.     }
  11.     public void initialize() throws IOException {
  12.       //初始化,分別執行個體化一個選取器,一個伺服器端可選擇通道
  13.       this.selector = Selector.open();
  14.       this.serverChannel = ServerSocketChannel.open();
  15.       this.serverChannel.configureBlocking(false);
  16.       InetAddress localhost = InetAddress.getLocalHost();
  17.       InetSocketAddress isa = new InetSocketAddress(localhost, this.port );
  18.       this.serverChannel.socket().bind(isa);//將該通訊端綁定到伺服器某一可用連接埠
  19.     }
  20.     //結束時釋放資源
  21.     public void finalize() throws IOException {
  22.         this.serverChannel.close();
  23.         this.selector.close();
  24.     }
  25.     //將讀入位元組緩衝的資訊解碼
  26.     public String decode( ByteBuffer byteBuffer ) throws 
  27. CharacterCodingException {
  28.         Charset charset = Charset.forName( "ISO-8859-1" );
  29.         CharsetDecoder decoder = charset.newDecoder();
  30.         CharBuffer charBuffer = decoder.decode( byteBuffer );
  31.         String result = charBuffer.toString();
  32.         return result;
  33.     }
  34.     //監聽連接埠,當通道準備好時進行相應操作
  35.     public void portListening() throws IOException, InterruptedException {
  36.       //伺服器端通道註冊OP_ACCEPT事件
  37.       SelectionKey acceptKey =this.serverChannel.register( this.selector,
  38.                                            SelectionKey.OP_ACCEPT );
  39.         //當有登入的事件發生時,select()傳回值將大於0
  40.         while (acceptKey.selector().select() > 0 ) {
  41.             System.out.println("event happened");
  42.             //取得所有已經準備好的所有選擇鍵
  43.             Set readyKeys = this.selector.selectedKeys();
  44.             //使用迭代器對選擇鍵進行輪詢
  45.             Iterator i = readyKeys.iterator();
  46.             while (i.hasNext()) {
  47.                 SelectionKey key = (SelectionKey)i.next();
  48.                 i.remove();//刪除當前將要處理的選擇鍵
  49.                 if ( key.isAcceptable() ) {//如果是有用戶端串連請求
  50.                     System.out.println("more client connect in!");
  51.                     ServerSocketChannel nextReady =
  52.                         (ServerSocketChannel)key.channel();
  53.                     //擷取用戶端通訊端
  54.                     Socket s = nextReady.accept();
  55.                     //設定對應的通道為非同步方式並註冊感興趣事件
  56.                     s.getChannel().configureBlocking( false );
  57.                     SelectionKey readWriteKey =
  58.                         s.getChannel().register( this.selector,
  59.                             SelectionKey.OP_READ|SelectionKey.OP_WRITE  );
  60.                     //將註冊的事件與該通訊端聯絡起來
  61. readWriteKey.attach( s );
  62. //將當前建立串連的用戶端通訊端及對應的通道存放在雜湊表//clientChannelMap中
  63.                     this.clientChannelMap.put( s, new 
  64. ClientChInstance( s.getChannel() ) );
  65.                     }
  66.                 else if ( key.isReadable() ) {//如果是通道讀準備好事件
  67.                     System.out.println("Readable");
  68.                     //取得選擇鍵對應的通道和通訊端
  69.                     SelectableChannel nextReady =
  70.                         (SelectableChannel) key.channel();
  71.                     Socket socket = (Socket) key.attachment();
  72.                     //處理該事件,處理方法已封裝在類ClientChInstance中
  73.                     this.readFromChannel( socket.getChannel(),
  74.                     (ClientChInstance)
  75. this.clientChannelMap.get( socket ) );
  76.                 }
  77.                 else if ( key.isWritable() ) {//如果是通道寫準備好事件
  78.                     System.out.println("writeable");
  79.                     //取得通訊端後處理,方法同上
  80.                     Socket socket = (Socket) key.attachment();
  81.                     SocketChannel channel = (SocketChannel) 
  82. socket.getChannel();
  83.                     this.writeToChannel( channel,"This is from server!");
  84.                 }
  85.             }
  86.         }
  87.     }
  88.     //對通道的寫操作
  89.     public void writeToChannel( SocketChannel channel, String message ) 
  90. throws IOException {
  91.         ByteBuffer buf = ByteBuffer.wrap( message.getBytes()  );
  92.         int nbytes = channel.write( buf );
  93.     }
  94.      //對通道的讀操作
  95.     public void readFromChannel( SocketChannel channel, ClientChInstance clientInstance )
  96.     throws IOException, InterruptedException {
  97.         ByteBuffer byteBuffer = ByteBuffer.allocate( BUFFERSIZE );
  98.         int nbytes = channel.read( byteBuffer );
  99.         byteBuffer.flip();
  100.         String result = this.decode( byteBuffer );
  101.         //當用戶端發出”@exit”退出命令時,關閉其通道
  102.         if ( result.indexOf( "@exit" ) >= 0 ) {
  103.             channel.close();
  104.         }
  105.         else {
  106.                 clientInstance.append( result.toString() );
  107.                 //讀入一行完畢,執行相應操作
  108.                 if ( result.indexOf( "\n" ) >= 0 ){
  109.                 System.out.println("client input"+result);
  110.                 clientInstance.execute();
  111.                 }
  112.         }
  113.     }
  114.     //該類封裝了怎樣對用戶端的通道進行操作,具體實現可以通過重載execute()方法
  115.     public class ClientChInstance {
  116.         SocketChannel channel;
  117.         StringBuffer buffer=new StringBuffer();
  118.         public ClientChInstance( SocketChannel channel ) {
  119.             this.channel = channel;
  120.         }
  121.         public void execute() throws IOException {
  122.             String message = "This is response after reading from channel!";
  123.             writeToChannel( this.channel, message );
  124.             buffer = new StringBuffer();
  125.         }
  126.         //當一行沒有結束時,將當前字竄置於緩衝尾
  127.         public void append( String values ) {
  128.             buffer.append( values );
  129.         }
  130.     }
  131.     //主程式
  132.     public static void main( String[] args ) {
  133.         NBlockingServer nbServer = new NBlockingServer(8000);
  134.         try {
  135.             nbServer.initialize();
  136.         } catch ( Exception e ) {
  137.             e.printStackTrace();
  138.             System.exit( -1 );
  139.         }
  140.         try {
  141.             nbServer.portListening();
  142.         }
  143.         catch ( Exception e ) {
  144.             e.printStackTrace();
  145.         }
  146.     }
  147. }

 

小結:

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

以上轉自http://space.itpub.net/17074730/viewspace-563262。。這的真心不錯。。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.