神奇好望角 The Magical Cape of Good Hope

          庸人不必自擾,智者何需千慮?
          posts - 26, comments - 50, trackbacks - 0, articles - 11
            BlogJava :: 首頁(yè) ::  :: 聯(lián)系 :: 聚合  :: 管理

          前面介紹了各種請(qǐng)求參數(shù)的注入,這些參數(shù)在 HTTP 請(qǐng)求中都是以純文本的方式存在的。在處理參數(shù)的時(shí)候,往往需要把這些文本參數(shù)轉(zhuǎn)換為 Java 對(duì)象。JAX-RS 提供了一些內(nèi)置的規(guī)則里自動(dòng)完成這種轉(zhuǎn)換。

          轉(zhuǎn)換規(guī)則一覽

          JAX-RS 提供了四條自動(dòng)類型轉(zhuǎn)換規(guī)則,下面我們逐條考察。

          原始類型

          這個(gè)早就見識(shí)過(guò)了,無(wú)需多說(shuō)。舉例回顧一下:

          @GET
          @Path("{id}")
          public Movie getXxx(@PathParam("id") int id) {/*...*/}
              

          提供接受單個(gè) String 參數(shù)的構(gòu)造器的類型

          這個(gè)也不難理解,JAX-RS 會(huì)自動(dòng)調(diào)用該構(gòu)造器創(chuàng)建一個(gè)對(duì)象:

          public class Style {
              public Style(String name) {/* ... */}
              // ...
          }
          
          @GET
          @Path("{name}")
          public Movie getXxx(@PathParam("name") Style style) {
              // JAX-RS 已自動(dòng)調(diào)用 xxx = new Style(name)
              // ...
          }
              

          提供靜態(tài)工廠方法 valueOf(String) 的類型

          也好理解。特別需要注意的是,所有的枚舉類型都在此列,因?yàn)榫幾g器會(huì)自動(dòng)給枚舉類型加上一個(gè)這樣的工廠方法。例如:

          public enum Style {/*...*/}
          
          @GET
          @Path("{name}")
          public Movie getXxx(@PathParam("name") Style style) {
              // JAX-RS 已自動(dòng)調(diào)用 style = Style.valueOf(name)
              // ...
          }
              

          類型參數(shù)滿足前兩個(gè)條件的 List<T>Set<T>SortedSet<T>

          這條規(guī)則適用于多值參數(shù),例如查詢參數(shù):

          @GET
          @Path("xxx")
          public Movie getXxx(@QueryParam("style") Set<Style> styles) {
              // JAX-RS 已自動(dòng)轉(zhuǎn)換每個(gè) Style 對(duì)象并組裝到 Set 中
              // ...
          }
              

          轉(zhuǎn)換失敗的處理

          如果轉(zhuǎn)換失敗,JAX-RS 會(huì)根據(jù)情況自動(dòng)拋出一個(gè)包裝了初始異常,但是帶不同 HTTP 錯(cuò)誤碼的 WebApplicationException:對(duì)矩陣參數(shù)(@MatrixParam)、查詢參數(shù) (@QueryParam)或路徑參數(shù)(@PathParam)來(lái)說(shuō)為 HTTP 404 找不到,而對(duì)頭部參數(shù)(@HeaderParam)或 Cookie 參數(shù)(@CookieParam)為 HTTP 400 錯(cuò)誤請(qǐng)求

          posted @ 2012-01-10 13:17 蜀山兆孨龘 閱讀(3204) | 評(píng)論 (2)編輯 收藏

          JDK 提供了對(duì) TCP(Transmission Control Protocol,傳輸控制協(xié)議)和 UDP(User Datagram Protocol,用戶數(shù)據(jù)報(bào)協(xié)議)這兩個(gè)數(shù)據(jù)傳輸協(xié)議的支持。本文開始探討 TCP。

          TCP 基礎(chǔ)知識(shí)

          在“服務(wù)器-客戶端”這種架構(gòu)中,服務(wù)器和客戶端各自維護(hù)一個(gè)端點(diǎn),兩個(gè)端點(diǎn)需要通過(guò)網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)交換。TCP 為這種需求提供了一種可靠的流式連接,流式的意思是傳出和收到的數(shù)據(jù)都是連續(xù)的字節(jié),沒有對(duì)數(shù)據(jù)量進(jìn)行大小限制。一個(gè)端點(diǎn)由 IP 地址和端口構(gòu)成(專業(yè)術(shù)語(yǔ)為“元組 {IP 地址, 端口}”)。這樣,一個(gè)連接就可以由元組 {本地地址, 本地端口, 遠(yuǎn)程地址, 遠(yuǎn)程端口} 來(lái)表示。

          連接過(guò)程

          在 TCP 編程接口中,端點(diǎn)體現(xiàn)為 TCP 套接字。共有兩種 TCP 套接字:主動(dòng)和被動(dòng),“被動(dòng)”狀態(tài)也常被稱為“偵聽”狀態(tài)。服務(wù)器和客戶端利用套接字進(jìn)行連接的過(guò)程如下:

          1. 服務(wù)器創(chuàng)建一個(gè)被動(dòng)套接字,開始循環(huán)偵聽客戶端的連接。
          2. 客戶端創(chuàng)建一個(gè)主動(dòng)套接字,連接服務(wù)器。
          3. 服務(wù)器接受客戶端的連接,并創(chuàng)建一個(gè)代表該連接的主動(dòng)套接字。
          4. 服務(wù)器和客戶端通過(guò)步驟 2 和 3 中創(chuàng)建的兩個(gè)主動(dòng)套接字進(jìn)行數(shù)據(jù)傳輸。

          下面是連接過(guò)程的圖解:

          TCP 連接
          TCP 連接

          一個(gè)簡(jiǎn)單的 TCP 服務(wù)器

          JDK 提供了 ServerSocket 類來(lái)代表 TCP 服務(wù)器的被動(dòng)套接字。下面的代碼演示了一個(gè)簡(jiǎn)單的 TCP 服務(wù)器(多線程阻塞模式),它不斷偵聽并接受客戶端的連接,然后將客戶端發(fā)送過(guò)來(lái)的文本按行讀取,全文轉(zhuǎn)換為大寫后返回給客戶端,直到客戶端發(fā)送文本行 bye

          public class TcpServer implements Runnable {
              private ServerSocket serverSocket;
          
              public TcpServer(int port) throws IOException {
                  // 創(chuàng)建綁定到某個(gè)端口的 TCP 服務(wù)器被動(dòng)套接字。
                  serverSocket = new ServerSocket(port);
              }
          
              @Override
              public void run() {
                  while (true) {
                      try {
                          // 以阻塞的方式接受一個(gè)客戶端連接,返回代表該連接的主動(dòng)套接字。
                          Socket socket = serverSocket.accept();
                          // 在新線程中處理客戶端連接。
                          new Thread(new ClientHandler(socket)).start();
                      } catch (IOException ex) {
                          ex.printStackTrace();
                      }
                  }
              }
          }
          
          public class ClientHandler implements Runnable {
              private Socket socket;
          
              public ClientHandler(Socket socket) {
                  this.socket = Objects.requireNonNull(socket);
              }
          
              @Override
              public void run() {
                  try (Socket s = socket) {  // 減少代碼量的花招……
                      // 包裝套接字的輸入流以讀取客戶端發(fā)送的文本行。
                      BufferedReader in = new BufferedReader(new InputStreamReader(
                              s.getInputStream(), StandardCharsets.UTF_8));
                      // 包裝套接字的輸出流以向客戶端發(fā)送轉(zhuǎn)換結(jié)果。
                      PrintWriter out = new PrintWriter(new OutputStreamWriter(
                              s.getOutputStream(), StandardCharsets.UTF_8), true);
          
                      String line = null;
                      while ((line = in.readLine()) != null) {
                          if (line.equals("bye")) {
                              break;
                          }
          
                          // 將轉(zhuǎn)換結(jié)果輸出給客戶端。
                          out.println(line.toUpperCase(Locale.ENGLISH));
                      }
                  } catch (IOException ex) {
                      ex.printStackTrace();
                  }
              }
          }
              

          阻塞模式的編程方式簡(jiǎn)單,但存在性能問(wèn)題,因?yàn)榉?wù)器線程會(huì)卡死在接受客戶端的 accept() 方法上,不能有效利用資源。套接字支持非阻塞模式,現(xiàn)在暫時(shí)略過(guò)。

          一個(gè)簡(jiǎn)單的 TCP 客戶端

          JDK 提供了 Socket 類來(lái)代表 TCP 客戶端的主動(dòng)套接字。下面的代碼演示了上述服務(wù)器的客戶端:

          public class TcpClient implements Runnable {
              private Socket socket;
          
              public TcpClient(String host, int port) throws IOException {
                  // 創(chuàng)建連接到服務(wù)器的套接字。
                  socket = new Socket(host, port);
              }
          
              @Override
              public void run() {
                  try (Socket s = socket) {  // 再次減少代碼量……
                      // 包裝套接字的輸出流以向服務(wù)器發(fā)送文本行。
                      PrintWriter out = new PrintWriter(new OutputStreamWriter(
                              s.getOutputStream(), StandardCharsets.UTF_8), true);
                      // 包裝套接字的輸入流以讀取服務(wù)器返回的文本行。
                      BufferedReader in = new BufferedReader(new InputStreamReader(
                              s.getInputStream(), StandardCharsets.UTF_8));
          
                      Console console = System.console();
                      String line = null;
                      while ((line = console.readLine()) != null) {
                          if (line.equals("bye")) {
                              break;
                          }
          
                          // 將文本行發(fā)送給服務(wù)器。
                          out.println(line);
                          // 打印服務(wù)器返回的文本行。
                          console.writer().println(in.readLine());
                      }
          
                      // 通知服務(wù)器關(guān)閉連接。
                      out.println("bye");
                  } catch (IOException ex) {
                      ex.printStackTrace();
                  }
              }
          }
              

          從 JDK 文檔可以看到,ServerSocketSocket 在初始化的時(shí)候,可以設(shè)定一些參數(shù),還支持延遲綁定。這些東西對(duì)性能和行為都有所影響。下一篇文章將詳解這兩個(gè)類的初始化。

          posted @ 2012-01-04 22:21 蜀山兆孨龘 閱讀(3083) | 評(píng)論 (5)編輯 收藏

          我竟然到現(xiàn)在才發(fā)現(xiàn)《Fundamental Networking in Java》這本神作,真有點(diǎn)無(wú)地自容的感覺。最近幾年做的都是所謂的企業(yè)級(jí)開發(fā),免不了和網(wǎng)絡(luò)打交道,但在實(shí)際工作中,往往會(huì)采用框架將底層細(xì)節(jié)和上層應(yīng)用隔離開,感覺就像是在一個(gè) Word 模板表單里面填寫內(nèi)容,做出來(lái)也沒什么成就感。雖然沒有不使用框架的理由,但我還真是有點(diǎn)懷念當(dāng)初直接用套接字做網(wǎng)絡(luò)編程的日子,既能掌控更多東西,還可以學(xué)到更多知識(shí),為研究框架的實(shí)現(xiàn)原理打基礎(chǔ)。閑話完畢,轉(zhuǎn)入今天的正題:IP(Internet Protocol,互聯(lián)網(wǎng)協(xié)議)。

          IP 基礎(chǔ)知識(shí)

          說(shuō)到 IP,大多數(shù)人的第一反應(yīng)估計(jì)都是 IP 地址。其實(shí) IP 是一種協(xié)議,IP 地址只是協(xié)議的一部分。《RFC 791 - INTERNET PROTOCOL》說(shuō):“互聯(lián)網(wǎng)協(xié)議是為在包交換計(jì)算機(jī)通信網(wǎng)絡(luò)的互聯(lián)系統(tǒng)中使用而設(shè)計(jì)的。”IP 包含三方面的功能:

          1. 用于查找主機(jī)的尋址系統(tǒng)
          2. 包格式的定義
          3. 傳輸和接收包的規(guī)則

          IP 的相關(guān) Java 類

          從 Java 的角度來(lái)看上面說(shuō)到的三個(gè)功能,只有第一個(gè)是開發(fā)人員需要關(guān)心的。另外兩個(gè)都依賴底層系統(tǒng)的實(shí)現(xiàn),JDK 也沒有提供相關(guān)的類去操作。下面一一介紹 JDK 提供的用于處理 IP 地址的類。

          InetAddress

          此類用來(lái)表示 IP 地址,它有兩個(gè)子類:Inet4AddressInet6Address,分別用于處理 IPv4 和 IPv6 兩個(gè)版本。在實(shí)際應(yīng)用中,InetAddress 足以應(yīng)付絕大多數(shù)情況。它提供了一些靜態(tài)方法來(lái)構(gòu)造實(shí)例,能根據(jù)參數(shù)格式自動(dòng)識(shí)別 IP 版本:

          public static InetAddress[] getAllByName(String host) throws UnknownHostException
          解析指定的主機(jī)地址,并返回其所有的 IP 地址;如果傳入 IP 地址字符串,則只會(huì)校驗(yàn)格式,返回的數(shù)組也只包含一個(gè)代表該 IP 地址的實(shí)例。例如,想看看谷歌有多少馬甲的話,InetAddress.getAllByName("www.google.com") 就可以了。
          public static InetAddress getByAddress(byte[] addr) throws UnknownHostException
          用表示 IP 地址的字節(jié)數(shù)組(專業(yè)術(shù)語(yǔ)稱為“原始 IP 地址”)構(gòu)造一個(gè)實(shí)例。IPv4 地址必須是 4 個(gè)字節(jié),IPv6 必須 16 個(gè)。不常用。
          public static InetAddress getByAddress(String host, byte[] addr) throws UnknownHostException
          用主機(jī)地址和原始 IP 地址構(gòu)造一個(gè)實(shí)例。此方法應(yīng)該慎用,因?yàn)樗粫?huì)對(duì)主機(jī)名進(jìn)行解析。即使主機(jī)名為 IP 地址字符串,也不會(huì)檢查是否與字節(jié)數(shù)組一致。
          public static InetAddress getByName(String host) throws UnknownHostException
          用主機(jī)地址構(gòu)造一個(gè)實(shí)例,也可以直接傳入 IP 地址字符串,等同于 getAllByName(host)[0]
          public static InetAddress getLocalHost() throws UnknownHostException
          返回本機(jī)在網(wǎng)絡(luò)中的地址。
          public static InetAddress getLoopbackAddress()
          返回環(huán)回地址 127.0.0.1,不拋出異常,等同于 getByName("localhost")(不要和 getLocalHost() 搞混)。環(huán)回地址使主機(jī)能夠自己連接自己,常被用來(lái)對(duì)在同一臺(tái)機(jī)器上測(cè)試網(wǎng)絡(luò)應(yīng)用程序。在 IPv4 中,環(huán)回地址的網(wǎng)段為 127.0.0.0/8,通常用 127.0.0.1;IPv6 中只有一個(gè) ::1

          接下來(lái)看看 InetAddress 中定義的部分實(shí)例方法:

          public byte[] getAddress()
          返回原始 IP 地址。
          public String getCanonicalHostName()
          返回全限定域名。這個(gè)方法可以用來(lái)探查實(shí)際的主機(jī)名,例如 InetAddress.getByName("www.google.com").getCanonicalHostName() 返回 we-in-f99.1e100.net
          public String getHostAddress()
          返回構(gòu)造時(shí)傳入的主機(jī)地址。
          public String getHostName()
          返回主機(jī)名。如果構(gòu)造時(shí)傳入的主機(jī)地址為 IP 地址字符串,則調(diào)用 getCanonicalHostName(),否則直接返回構(gòu)造時(shí)傳入的主機(jī)地址。
          public boolean isAnyLocalAddress()
          檢查是否為通配符地址。通配符地址為 0.0.0.0(IPv4)或 ::0(IPv6),代表所有的本地 IP 地址。例如,假設(shè)電腦有兩塊網(wǎng)卡,各有一個(gè)地址,如果想讓一個(gè)程序同時(shí)監(jiān)聽這兩個(gè)地址,就需要用通配符地址。
          public boolean isLinkLocalAddress()
          檢查是否為鏈路本地地址。IPv4 里定義為地址段 169.254.0.0/16,Ipv6 里是以 fe80::/64 為前綴的地址。在電腦沒聯(lián)網(wǎng)的時(shí)候查看本機(jī) IP,就能看到這種地址。
          public boolean isLoopbackAddress()
          檢查是否為環(huán)回地址。
          public boolean isSiteLocalAddress()
          檢查是否為站點(diǎn)本地地址。站點(diǎn)本地地址這個(gè)名詞實(shí)際上已經(jīng)過(guò)時(shí)了,現(xiàn)在叫唯一本地地址。IPv4 中未定義;IPv6 中定義為地址段 fc00::/7。這些地址用于私有網(wǎng)絡(luò),例如企業(yè)內(nèi)部的局域網(wǎng)。

          此外還有一些有關(guān)多播地址的方法,暫時(shí)略過(guò)。

          JDK 默認(rèn)同時(shí)支持 IPv4 和 IPv6。如果只想使用一種,可以根據(jù)情況將 java.net.preferIPv4Stackjava.net.preferIPv6Addresses 這兩個(gè)系統(tǒng)屬性之一設(shè)為 true。兩個(gè)屬性的默認(rèn)值都為 false。一般來(lái)說(shuō)不需要去驚動(dòng)它們。

          SocketAddress

          該類是一個(gè)空殼,事實(shí)上應(yīng)用程序使用的是它的唯一子類 InetSocketAddress,目前還看不出這樣設(shè)計(jì)有什么意義。該類只不過(guò)在 InetAddress 的基礎(chǔ)上增加了一個(gè)端口屬性。

          NetworkInterface

          該類代表網(wǎng)絡(luò)接口,例如一塊網(wǎng)卡。一個(gè)網(wǎng)絡(luò)接口可以綁定一些 IP 地址。具有多個(gè)網(wǎng)絡(luò)接口的主機(jī)被稱為多宿主主機(jī)。下面的代碼可打印出所有本機(jī)網(wǎng)絡(luò)接口的信息:

          for (NetworkInterface ni : Collections.list(NetworkInterface.getNetworkInterfaces())) {
              System.out.println(ni);
              for (InterfaceAddress ia : ni.getInterfaceAddresses()) {
                  System.out.println("\t" + ia);
              }
              System.out.println();
          }
              

          在我的筆記本上運(yùn)行結(jié)果為:

          name:lo (Software Loopback Interface 1)
          	/127.0.0.1/8 [/127.255.255.255]
          	/0:0:0:0:0:0:0:1/128 [null]
          name:net0 (WAN Miniport (SSTP))
          name:net1 (WAN Miniport (L2TP))
          name:net2 (WAN Miniport (PPTP))
          name:ppp0 (WAN Miniport (PPPOE))
          name:eth0 (WAN Miniport (IPv6))
          name:eth1 (WAN Miniport (Network Monitor))
          name:eth2 (WAN Miniport (IP))
          name:ppp1 (RAS Async Adapter)
          name:net3 (WAN Miniport (IKEv2))
          name:net4 (Intel(R) Wireless WiFi Link 4965AGN)
          	/fe80:0:0:0:288a:2daf:3549:1811%11/64 [null]
          name:eth3 (Broadcom NetXtreme 57xx Gigabit Controller)
          	/10.140.1.133/24 [/10.140.1.255]
          	/fe80:0:0:0:78c7:e420:1739:f947%12/64 [null]
          name:net5 (Teredo Tunneling Pseudo-Interface)
          	/fe80:0:0:0:e0:0:0:0%13/64 [null]
          name:net6 (Bluetooth Device (RFCOMM Protocol TDI))
          name:eth4 (Bluetooth Device (Personal Area Network))
          name:eth5 (Cisco AnyConnect VPN Virtual Miniport Adapter for Windows x64)
          name:net7 (Microsoft ISATAP Adapter)
          	/fe80:0:0:0:0:5efe:a8c:185%17/128 [null]
          name:net8 (Microsoft ISATAP Adapter #2)
          name:net9 (Intel(R) Wireless WiFi Link 4965AGN-QoS Packet Scheduler-0000)
          name:eth6 (Broadcom NetXtreme 57xx Gigabit Controller-TM NDIS Sample LightWeight Filter-0000)
          name:eth7 (Broadcom NetXtreme 57xx Gigabit Controller-QoS Packet Scheduler-0000)
          name:eth8 (Broadcom NetXtreme 57xx Gigabit Controller-WFP LightWeight Filter-0000)
          name:eth9 (WAN Miniport (Network Monitor)-QoS Packet Scheduler-0000)
          name:eth10 (WAN Miniport (IP)-QoS Packet Scheduler-0000)
          name:eth11 (WAN Miniport (IPv6)-QoS Packet Scheduler-0000)
          name:net10 (Intel(R) Wireless WiFi Link 4965AGN-Native WiFi Filter Driver-0000)
          name:net11 (Intel(R) Wireless WiFi Link 4965AGN-TM NDIS Sample LightWeight Filter-0000)
          name:net12 (Intel(R) Wireless WiFi Link 4965AGN-WFP LightWeight Filter-0000)

          posted @ 2011-12-30 17:39 蜀山兆孨龘 閱讀(2710) | 評(píng)論 (0)編輯 收藏

          《JAX-RS 從傻逼到牛叉 3:路徑匹配》中,我們已經(jīng)見過(guò)如何使用 @PathParam@QueryParam@MatrixParam 分別注入 URI 中的路徑參數(shù)、矩陣參數(shù)和查詢參數(shù),以及如何編程訪問(wèn)這些參數(shù)。本文介紹表單參數(shù)、HTTP 頭部參數(shù)和 Cookie 參數(shù)的注入。

          表單參數(shù)

          HTTP 請(qǐng)求也可以使用提交表單的方式。這時(shí)請(qǐng)求方法一般是 POST,當(dāng)然春哥也無(wú)法阻止你用 GET。在前面我們雖然介紹過(guò)處理 POST 請(qǐng)求的例子,但那只是利用了 JAX-RS 對(duì) JAXB 的支持,并沒有涉及到對(duì)具體請(qǐng)求參數(shù)的注入。JAX-RS 提供了 @FormParam 注解來(lái)注入 POST 請(qǐng)求的參數(shù),例如:

          @POST
          public Response createMovie(@FormParam("title") String title) {
              // 此處省略若干行
          }
              

          這兒省略了 @Consumes 注解,JAX-RS 會(huì)自動(dòng)默認(rèn)為 @Consumes(MediaType.APPLICATION_FORM_URLENCODED),也就是 application/x-www-form-urlencoded 格式的請(qǐng)求。如果請(qǐng)求格式為 multipart/form-data,就必須顯示指明:

          @POST
          @Consumes(MediaType.MULTIPART_FORM_DATA)
          public Response createMovie(@FormParam("title") String title) {
              // 此處省略若干行
          }
              

          JAX-RS 還支持文件的上傳和下載,以后再介紹。

          HTTP 頭部參數(shù)

          注入 HTTP 頭部參數(shù)簡(jiǎn)單得不能再簡(jiǎn)單了:

          @GET
          @Path("xxx")
          @Produces(MediaType.TEXT_PLAIN)
          public String xxx(@HeaderParam("User-Agent") String userAgent) {
              // 此處省略若干行
          }
              

          如果有很多頭部參數(shù),為了避免臃腫的參數(shù)列表,可以注入一個(gè)頭部對(duì)象,然后編程訪問(wèn)頭部參數(shù):

          @GET
          @Path("xxx")
          @Produces(MediaType.TEXT_PLAIN)
          public String xxx(@Context HttpHeaders headers) {
              // 此處省略若干行
          }
              

          Cookie 參數(shù)

          注入 Cookie 參數(shù)同樣的簡(jiǎn)單:

          @GET
          @Path("xxx")
          @Produces(MediaType.TEXT_PLAIN)
          public String xxx(@CookieParam("userName") String userName) {
              // 此處省略若干行
          }
              

          如果希望編程訪問(wèn),則可以像編程訪問(wèn)那樣注入一個(gè) HttpHeaders 對(duì)象,然后通過(guò)它的 getCookies() 方法來(lái)獲取所有的 Cookie。

          posted @ 2011-12-29 16:34 蜀山兆孨龘 閱讀(4414) | 評(píng)論 (4)編輯 收藏

          Exchanger 用來(lái)讓兩個(gè)線程互相等待并交換計(jì)算結(jié)果。這個(gè)類的用法很簡(jiǎn)單,因?yàn)樗投x了兩個(gè)重載的 exchange 方法,參數(shù)多的那個(gè)無(wú)非增加了對(duì)超時(shí)的支持。當(dāng)一個(gè)線程調(diào)用 exchange 的時(shí)候(以計(jì)算結(jié)果作為參數(shù)),它就開始等待另一個(gè)線程調(diào)用 exchange,然后兩個(gè)線程分別收到對(duì)方調(diào)用 exchange 時(shí)傳入的參數(shù),從而完成了計(jì)算結(jié)果的交換。

          不用太多的解釋,運(yùn)行下面這個(gè)例子就一清二楚:

          final Exchanger<String> e = new Exchanger<>();
          
          new Thread() {
              @Override
              public void run() {
                  long id = Thread.currentThread().getId();
                  String s = "abc";
                  System.out.println("線程 [" + id + "] 算出 " + s);
          
                  try {
                      TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                      System.out.println("線程 [" + id + "] 收到 " + e.exchange(s));
                  } catch (InterruptedException ex) {
                      ex.printStackTrace();
                  }
              }
          }.start();
          
          new Thread() {
              @Override
              public void run() {
                  long id = Thread.currentThread().getId();
                  String s = "xyz";
                  System.out.println("線程 [" + id + "] 算出 " + s);
          
                  try {
                      TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                      System.out.println("線程 [" + id + "] 收到 " + e.exchange(s));
                  } catch (InterruptedException ex) {
                      ex.printStackTrace();
                  }
              }
          }.start();
              

          運(yùn)行結(jié)果(可能為):

          線程 [9] 算出 abc
          線程 [10] 算出 xyz
          線程 [10] 收到 abc
          線程 [9] 收到 xyz

          最后強(qiáng)調(diào)下,該類只適用于兩個(gè)線程,妄圖用它來(lái)處理多個(gè)生產(chǎn)者和消費(fèi)者之間的數(shù)據(jù)交換是注定要失敗的……

          posted @ 2011-12-27 10:50 蜀山兆孨龘 閱讀(1526) | 評(píng)論 (0)編輯 收藏

          僅列出標(biāo)題
          共8頁(yè): 上一頁(yè) 1 2 3 4 5 6 7 8 下一頁(yè) 
          主站蜘蛛池模板: 刚察县| 营口市| 张家港市| 宿州市| 阿图什市| 孝义市| 汨罗市| 铁岭市| 千阳县| 明星| 布尔津县| 霍州市| 吉林市| 杭锦后旗| 万源市| 宝兴县| 阜南县| 博野县| 宁安市| 江山市| 察哈| 个旧市| 东明县| 双江| 建水县| 含山县| 金塔县| 富锦市| 民乐县| 封开县| 吴忠市| 广东省| 龙海市| 漯河市| 格尔木市| 磐石市| 缙云县| 上犹县| 岳西县| 恩平市| 弥渡县|