??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲韩国—中文字幕,在线亚洲人成电影网站色www,人人狠狠综合久久亚洲高清http://www.tkk7.com/shinzey/category/44813.html思h不必自扰Q智者何需千虑Q?/description>zh-cnThu, 16 Feb 2012 11:54:55 GMTThu, 16 Feb 2012 11:54:55 GMT60非主ƈ发工具之 ForkJoinPoolhttp://www.tkk7.com/shinzey/archive/2012/02/09/368312.html蜀山兆孨龘蜀山兆孨龘Thu, 09 Feb 2012 02:40:00 GMThttp://www.tkk7.com/shinzey/archive/2012/02/09/368312.htmlhttp://www.tkk7.com/shinzey/comments/368312.htmlhttp://www.tkk7.com/shinzey/archive/2012/02/09/368312.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/368312.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/368312.html

ForkJoinPool ?Java SE 7 新功能“分?l合框架”的核心c,现在可能乏h问|Q但我觉得它q早会成Z。分?l合框架是一个比较特D的U程池框Ӟ专用于需要将一个Q务不断分解成子Q务(分叉Q,再不断进行汇d到最l结果(l合Q的计算q程。比起传l的U程池类 ThreadPoolExecutorQ?code>ForkJoinPool 实现了工作窃取算法,使得I闲U程能够d分担从别的线E分解出来的子Q务,从而让所有的U程都尽可能处于饱满的工作状态,提高执行效率?/p>

ForkJoinPool 提供了三cL法来调度子Q务:

execute pd
异步执行指定的Q务?/dd>
invoke ?invokeAll
执行指定的Q务,{待完成Q返回结果?/dd>
submit pd
异步执行指定的Q务ƈ立即q回一?Future 对象?/dd>

子Q务由 ForkJoinTask 的实例来代表。它是一个抽象类QJDK 为我们提供了两个实现Q?code>RecursiveTask ?RecursiveActionQ分别用于需要和不需要返回计结果的子Q务?code>ForkJoinTask 提供了三个静态的 invokeAll Ҏ来调度子dQ注意只能在 ForkJoinPool 执行计算的过E中调用它们?/p>

ForkJoinPool ?ForkJoinTask q提供了很多让hD~ؕ的公共方法,其实它们大多数都是其内部实现去调用的Q对于应用开发h员来说意义不大?/p>

下面以统?D 盘文件个Cؓ例。这实际上是对一个文件树的遍历,我们需要递归地统计每个目录下的文件数量,最后汇总,非常适合用分?l合框架来处理:

// 处理单个目录的Q?
public class CountingTask extends RecursiveTask<Integer> {
    private Path dir;

    public CountingTask(Path dir) {
        this.dir = dir;
    }

    @Override
    protected Integer compute() {
        int count = 0;
        List<CountingTask> subTasks = new ArrayList<>();

        // d目录 dir 的子路径?
        try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
            for (Path subPath : ds) {
                if (Files.isDirectory(subPath, LinkOption.NOFOLLOW_LINKS)) {
                    // Ҏ个子目录都新Z个子d?
                    subTasks.add(new CountingTask(subPath));
                } else {
                    // 遇到文gQ则计数器增?1?
                    count++;
                }
            }

            if (!subTasks.isEmpty()) {
                // 在当前的 ForkJoinPool 上调度所有的子Q务?
                for (CountingTask subTask : invokeAll(subTasks)) {
                    count += subTask.join();
                }
            }
        } catch (IOException ex) {
            return 0;
        }
        return count;
    }
}

// 用一?ForkJoinPool 实例调度“MQ务”,然后敬请期待l果…?
Integer count = new ForkJoinPool().invoke(new CountingTask(Paths.get("D:/")));
    

在我的笔记本上,l多ơ运行这D代码,耗费的时间稳定在 600 豪秒左右。普通线E池Q?code>Executors.newCachedThreadPool()Q耗时 1100 毫秒左右Q见工作窃取的优势?/p>

l束本文前,我们来围观一个最奇的结果:单线E算法(使用 Files.walkFileTree(...)Q比q两个都快,q_耗时 550 毫秒Q这警告我们q引入多线E就能优化性能Qƈ要先经q多ơ测试才能下l论?/p>

蜀山兆孨龘 2012-02-09 10:40 发表评论
]]>
Java |络~程从菜鸟到叫兽 4Q面向流的套接字 I/Ohttp://www.tkk7.com/shinzey/archive/2012/01/19/368751.html蜀山兆孨龘蜀山兆孨龘Thu, 19 Jan 2012 06:37:00 GMThttp://www.tkk7.com/shinzey/archive/2012/01/19/368751.htmlhttp://www.tkk7.com/shinzey/comments/368751.htmlhttp://www.tkk7.com/shinzey/archive/2012/01/19/368751.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/368751.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/368751.html

前面已经看到Q?code>Socket cȝ getInputStream() ?getOutStream() Ҏ分别获取套接字的输入和输出。输入流用来dq端发送过来的数据Q输出流则用来向q端发送数据?/p>

输入?/h1>

使用套接字的输入读取数据时Q当前线E会q入d状态,直到套接字收C些数据ؓ止(亦即套接字的接收~冲区有可用数据Q。该输入的 available() Ҏ只是q回接收~冲区的可用字节数量Q不可能知道q端q要发送多字节。用输入流的时候,最好先它包装Z?BufferedInputStreamQ因取接收缓冲区导?JVM 和底层系l之间的切换Q应当尽量减切换次C提高性能?code>BufferedInputStream 的缓冲区大小最好设为套接字接收~冲区的大小?/p>

如果直接调用输入的 close() Ҏ来关闭它Q则导致套接字被关闭。对此,Socket cL供了一?shutdownInput() Ҏ来禁用输入流。调用该Ҏ后,每次L作都返?EOFQ无法再dq端发送的数据。对q个 EOF 的检,不同的输入流包装体现Z同的l果Q可能读?-1 个字节,可能d的字W串?nullQ还可能收到一?EOFException {等。禁用输入流后,q端输出的行ؓ是^台相关的Q?/p>

  • ?BSD q_上,q端的发送的数据能正常接Ӟ然后直接丢弃。远端无法知道本端的输入已用。这?JDK 文档描述的行Z致?/li>
  • ?WINSOCK q_上,q端发送数据将会导致“连接被重置”的错误?/li>
  • ?Linux q_上,q端发送的数据能l接Ӟ直到套接字输入缓冲区填满Q之后远端再也无法发送数据(若用阻塞模式则q入死锁Q?/li>

用输入这U技术ƈ不常用?/p>

输出?/h1>

套接字的输出操作实际上仅仅将数据写到发送缓冲区内,当发送缓冲区填满且上ơ的发送成功后Q由底层pȝ负责发送。如果发送缓冲区的剩余空间不够,当前U程׃d。和输入类|最好将输出包装ؓ BufferedOutputStream?/p>

如果套接字的双发都?ObjectInputStream ?ObjectOutputStream 来读?Java 对象Q则必须先创?ObjectOutputStreamQ因?ObjectInputStream 在构造的时候会试图d对象头部Q如果双发都先创?ObjectInputStreamQ则会互相等待对方的输出Q造成死锁Q?/p>

// 创徏的顺序不能颠倒!
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
    

cM于输入流Q关闭输出流也导致关闭套接字Q所?Socket cdh供了一?shutdownOutput() 来禁用输出流。禁用输出流后,已写入发送缓冲区的数据会正常发送,之后的Q何写操作都会D IOExceptionQ且q端的输入流始终会读?EOF。禁用输出流非常有用Q例如套接字的双发都在发送完毕数据后用输入,然后双方都会收到 EOFQ从而知道数据已l全部交换完毕,可以安全关闭套接字。直接关闭套接字会同时关闭输入流和输出流Q且断开q接Q达不到q种效果?/p>

使用的d套接字的优缺?/h2>

如果要用流q行输入和输出,只能用d模式的套接字。这里ȝ一下阻塞套接字的优~点。先看看优点Q?/p>

  1. ~程模型单,非常适合初学者上手?/li>
  2. 以装饰器模式设计?Java I/O 使得开发h员可以轻村֜?I/O 读写Q何类型的数据?/li>

但在性能斚w有致命的~点Q?/p>

  1. ׃服务器套接字接受q接Q以及套接字的读写都会阻塞,性能低下?/li>
  2. 如果不对 I/O 手动进行缓Ԍ则可能造成一ơ只处理一个字节,性能低下?/li>
  3. 服务器套接字每次只能接受一个连接,D JVM 和底层系l之间频J的调用切换Q性能低下?/li>

下一文章开始探讨用基?NIO 的套接字通道和缓冲区实现伸羃性更强的 TCP 套接字?/p>

蜀山兆孨龘 2012-01-19 14:37 发表评论
]]>Java |络~程从菜鸟到叫兽 3Q套接字初始化详?/title><link>http://www.tkk7.com/shinzey/archive/2012/01/16/368393.html</link><dc:creator>蜀山兆孨龘</dc:creator><author>蜀山兆孨龘</author><pubDate>Mon, 16 Jan 2012 02:31:00 GMT</pubDate><guid>http://www.tkk7.com/shinzey/archive/2012/01/16/368393.html</guid><wfw:comment>http://www.tkk7.com/shinzey/comments/368393.html</wfw:comment><comments>http://www.tkk7.com/shinzey/archive/2012/01/16/368393.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.tkk7.com/shinzey/comments/commentRss/368393.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/shinzey/services/trackbacks/368393.html</trackback:ping><description><![CDATA[<div id="t9nn99l" class="article"> <p><code>ServerSocket</code> cd <code>Socket</code> c都提供了多个公共构造方法。不同的构造方法不仅带的参C同,所h的意义也不一栗下面分别解析这两个cȝ实例初始化过E?/p> <h1><code>ServerSocket</code> 实例的初始化</h1> <p><code>ServerSocket</code> cL供了四个构造器Q?/p> <dl> <dt><code>public ServerSocket(int port) throws IOException</code></dt> <dt><code>public ServerSocket(int port, int backlog) throws IOException</code></dt> <dt><code>public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException</code></dt> <dt><code>public ServerSocket() throws IOException</code></dt> </dl> <p>带参构造器用来创徏已绑定的服务器套接字Q也是说构造成功后它就已经开始侦听指定的端口Q且能够调用 <code>accept()</code> Ҏ来接受客Lq接。默认构造器则会创徏未绑定的服务器套接字Q构造成功后必须手动其l定C个本地地址才能用,在绑定之前可以进行一些选项配置?/p> <h2>带参构造器</h2> <p>ȝ来说Q带参构造器提供了三个参敎ͼ</p> <dl> <dt><code>port</code></dt> <dd>指定该服务器套接字所要侦听的本地端口。如果ؓ 0Q则ql自动分配一个端口号Q这必须以另外的方式让客L获取端口受?/dd> <dt><code>backlog</code></dt> <dd>q个名词目前q没有合适的译名。底层系l的 TCP 实现会维护一个连接队列,该队列缓存了已被 TCP 处理完毕Q但q没有被服务器套接字接受的客Lq接。一旦某个连接被接受Q通过调用 <code>accept()</code> ҎQ,它就会被从队列中U除?code>backlog</code> 参数q于指定队列的最大长度,默认gؓ 50Q但q个值只是一个徏议,底层pȝ可能Ҏ需要自动调整。如果队列满了,则其行ؓ是^台相关的Q微软的 WINSOCK 会拒l新的连接,其他实现则什么都不做。严格地_微Y没有遵守规范Q破坏了游戏规则…?/dd> <dt><code>bindAddr</code></dt> <dd>一台机器可能会有多个本?IP 地址Q例如同时用多块网卡。用其他两个带参构造器Ӟ该参Cؓ <code>null</code>Q服务器套接字会在所有的本地 IP 地址Q?code>0.0.0.0</code> ?<code>::0</code>Q上侦听。如果希望只侦听一个地址Q则可用该参数?/dd> </dl> <h2>默认构造器</h2> <p>如果使用默认构造器Q在l定地址前,q可以做些配|。绑定操作由两个 <code>bind</code> Ҏ定义Q参数类g带参构造器。配|项包括以下斚wQ都必须在绑定前配置Q:</p> <dl> <dt>讄是否重用本地地址</dt> <dd>该选项?<code>setReuseAddress(boolean on)</code> Ҏ配置Q对应底层系l的 <code>SO_REUSEADDR</code> 套接字选项。JDK 没有定义该选项的默认倹{如果该选项?<code>false</code>Q则在关?TCP q接ӞZ保证可靠性,该连接可能在关闭后的一D|_大约两分钟)内保持超时状态(通常UCؓ <code>TIME_WAIT</code> 状态或 <code>2MSL</code> {待状态)Q这D|间里无法新建的服务器套接字l定到同一个地址。在开发阶D,服务器可能不断重启,打开攚w项会非常有用?/dd> <dt>讄接收~冲区大?/dt> <dd>该选项?<code>setReceiveBufferSize(int size)</code> Ҏ配置Q对应底层系l的 <code>SO_RCVBUF</code> 套接字选项Q单位是字节。《RFC 1323 - TCP Extensions for High Performance》将~冲区大定义ؓ 64KB。该选项只是一个徏议|底层pȝ可能Ҏ需要自行调整?/dd> <dt>讄时?/dt> <dd>该选项?<code>setSoTimeout(int timeout)</code> Ҏ配置Q对应底层系l的 <code>SO_TIMEOUT</code> 套接字选项Q单位是毫秒。默认gؓ 0。该选项影响 <code>accept</code> Ҏ的阻塞时间长度,如果时引?<code>SocketTimeoutException</code>。如果设?0Q则表示怸时?/dd> <dt>讄性能首选项</dt> <dd>性能首选项包括q接旉、gq和带宽三个选项Q由 <code>setPerformancePreferences(int connectionTime, int latency, int bandwidth) </code> Ҏ配置。这三个数值分别表C短q接旉、低延迟和高带宽的相寚w要性,数D大则重要;其各自的l对值没有意义。该Ҏ的初hZ?Java 能在用非 TCP/IP 实现的套接字环境下工作得更好Q某些需要对|络q行调优的程序也可以这三个首选项作ؓ配置参数提供l用戗?/dd> </dl> <h1><code>Socket</code> 实例的初始化</h1> <p><code>Socket</code> cL供了六个公共构造器Q已q时的除外)Q?/p> <dl> <dt><code>public Socket(String host, int port) throws UnknownHostException, IOException</code></dt> <dt><code>public Socket(InetAddress address, int port) throws IOException</code></dt> <dt><code>public Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException</code></dt> <dt><code>public Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException</code></dt> <dt><code>public Socket()</code></dt> <dt><code>public Socket(Proxy proxy)</code></dt> </dl> <p>前四个构造器创徏已连接的客户端套接字Q也是说构造的时候就会去q接服务器。前两个构造器需要提供服务器的地址和端口作为参敎ͼ本地地址和端口由pȝ自动分配Q后两个允许手动指定本地地址和端口,但极用。后两个构造器创徏未连接的套接字,创徏后需要调?<code>connect</code> Ҏ手动q接Q连接之前可以做一些配|。最后一个构造器接受一个代表代理服务其?<code>Proxy</code> 对象QJDK 支持 HTTP ?SOCKSQV4 ?V5Q两U代理类型?/p> <h2>q接前的配置</h2> <p>在连接前Q客L套接字不仅像服务器套接字那样可以讄是否重用本地地址、缓冲区大小、超时值和性能首选项Q还能够配置以下各项Q都必须在连接前配置Q:</p> <dl> <dt>讄是否保持z跃</dt> <dd>该选项?<code>setKeepAlive(boolean on)</code> Ҏ配置Q对应底层系l的 <code>SO_KEEPALIVE</code> 套接字选项。默认gؓ <code>false</code>。如果打开该选项Q则套接字会定期自动发送保持活跃的探测性消息,cM于心x。根据《RFC 1122 - Requirements for Internet Hosts》的规定Q保持活跃机制只?TCP 的一个可选功能,如果支持的话Q默认必Mؓ <code>false</code>Q而且q种机制默认在成功徏立连接后Q且q箋两小时没有数据传输的情况下才会被ȀzR从另一斚w来看Q通过套接字的 I/O 操作完全可以知道q接是否q有效,所以该选项的实用hg大?/dd> <dt>讄是否收发带外数据</dt> <dl>该选项?<code>setOOBInline(boolean on)</code> Ҏ配置Q对应底层系l的 <code>SO_OOBINLINE</code> 套接字选项。默认gؓ <code>off</code>。带外数据(Out-of-band DataQ也叫做紧急数据,表示数据很重要,需要用不同于发送普通数据的一个专用通道来发送。打开该选项后,可以调?<code>sendUrgentData(int data)</code> Ҏ发送一个字节的紧急数据。JDK 对带外数据只提供了有限支持,紧急数据将会和普通数据一赯收到Qƈ且无法自动区分。该选项对应用开发h员意义不大?/dl> <dt>讄是否从容关闭q接</dt> <dd>该选项?<code>setSoLinger(boolean on, int linger)</code> Ҏ配置Q对应底层系l的 <code>SO_LINGER</code> 套接字选项。默认ؓ <code>false</code>。该选项只会影响套接字的关闭Q其中的 <code>linger</code> 参数表示时旉Q单位ؓU。如果打开攚w项Q如果将 <code>linger</code> 设ؓ 0Q则关闭套接字的时候,未发送的数据会被丢弃Q且另一端会出现q接被同位体重置的异常;如果 <code>linger</code> ?0Q则关闭套接字的U程被dQ直到数据全部发送或时Q超时后的行Z底层pȝ相关QJDK 无法控制。如果关闭该选项Q则套接字正常关闭,数据也会全部发送。由于底层实现的差异性,不提倡应用开发h员打开该选项?/dd> <dt>讄是否延迟发送数?/dt> <dd>该选项?<code>setTcpNoDelay(boolean on)</code> Ҏ配置Q对应底层系l的 <code>TCP_NODELAY</code> TCP 选项。默认gؓ <code>off</code>。打开该选项禁?Nagle 法QTCP 包会立即发送;关闭该选项则会启用 Nagle 法Q多个较的 TCP 包会被组合成一个大包一起发送,虽然发送gq了Q但有利于避免网l拥塞。默认ؓ <code>false</code>。该选项对实时性很强的E序可能有用Q但一般的E序不需要关心?/dd> <dt>讄量cd</dt> <dd>该选项?<code>setTrafficClass(int tc)</code> Ҏ配置Q对应底层系l的“流量类别”套接字属性。该选项用于向网l(例如路由器)提示从该套接字发送的包需要获取哪些服务类型,Ҏ?TCP 协议栈没有媄响。IPv4 ?IPv6 分别定义了多个不同的|例如 IPv4 ?<code>0x08</code> 定义为最大吞吐量Q?code>0x10</code> 定义为最gq,{等。可以用或运将多个值合qؓ一个选项。该选项用来调整性能Q需要根据实际情况设|。由于只是徏议|可能被网l忽略?/dd> </dl> </div><img src ="http://www.tkk7.com/shinzey/aggbug/368393.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/shinzey/" target="_blank">蜀山兆孨龘</a> 2012-01-16 10:31 <a href="http://www.tkk7.com/shinzey/archive/2012/01/16/368393.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>单例模式的一个疑?/title><link>http://www.tkk7.com/shinzey/archive/2012/01/10/368251.html</link><dc:creator>蜀山兆孨龘</dc:creator><author>蜀山兆孨龘</author><pubDate>Tue, 10 Jan 2012 09:39:00 GMT</pubDate><guid>http://www.tkk7.com/shinzey/archive/2012/01/10/368251.html</guid><wfw:comment>http://www.tkk7.com/shinzey/comments/368251.html</wfw:comment><comments>http://www.tkk7.com/shinzey/archive/2012/01/10/368251.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.tkk7.com/shinzey/comments/commentRss/368251.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/shinzey/services/trackbacks/368251.html</trackback:ping><description><![CDATA[<div id="bx333f9" class="article"> <p>|上很多关于单例模式写法的文章,不外乎饿汉和懒汉两种形式的讨论。很多h喜欢用懒汉式Q因得它实现了gq加载,可以让系l的性能更好。但事实果真如此吗?我对此存疑?/p> <p>首先我们查一下饿汉和懒汉单例模式最单的写法Q这里不讨论哪种懒汉写法更好Q:</p> <pre class="brush: java"> // 饿汉 public final class HungrySingleton { private static final HungrySingleton INSTANCE = new HungrySingleton(); private HungrySingleton() { System.out.println("Initializing..."); } public static HungrySingleton getInstance() { return INSTANCE; } } // 懒汉 public final class LazySingleton { private static LazySingleton INSTANCE; private LazySingleton() { System.out.println("Initializing..."); } public static synchronized LazySingleton getInstance() { if (INSTANCE == null) { INSTANCE = new LazySingleton(); } return INSTANCE; } } </pre> <p>从理Z来说Q?code>HungrySingleton</code> 的单例在该类W一ơ用的时候创建,?<code>LazySingleton</code> 的单例则在其 <code>getInstance()</code> Ҏ被调用的时候创建。至于网上有人声U“饿汉式不管用不用都会初始化”,U属走\的时候步子迈得太大。谁的加载更q?如果你只是调用它们的 <code>getInstance()</code> Ҏ来得到单例对象,则它们都是gq加载,q样懒汉式没有Q何意义,而且׃ <code>LazySingleton</code> 采取了同步措施,性能更低Q可以说M懒汉式的性能都低于饿汉式Q。当你用一个单例类的时候,NW一步不是调?<code>getInstance()</code> 么?所以在自己的代码里Q我更喜Ƣ用饿汉式?/p> <p>下面用一个例子来试加蝲序Q?/p> <pre class="brush: java"> // 饿汉 System.out.println("Before"); HungrySingleton.getInstance(); System.out.println("After"); // 懒汉 System.out.println("Before"); LazySingleton.getInstance(); System.out.println("After"); </pre> <p>输出l果都是Q?/p> <pre class="output">Before Initializing... After</pre> <p>那么Q懒汉模式还有什么存在意义?如果pȝ使用了某些需要在启动时对c进行扫描的框架Q用饿汉式的话Q启动时间比懒汉式更长,如果使用了大量单例类Q不利于开发阶Dc在pȝ的正式运行阶D,所有的单例c迟早都要加载的Qȝ说来两者性能持^Q但是懒汉式每次都至多一个判断,所以越到后期越体现饿汉的优性?/p> <p>最后,推荐下《Effective Java》第二版指出的用枚Dcd实现的饿汉单例模式:</p> <pre class="brush: java"> // 饿汉 public enum HungrySingleton { INSTANCE; private HungrySingleton() { } } </pre> <p>q种写法不但最z,q能L扩展为实例数量固定的“多例模式”?/p> </div><img src ="http://www.tkk7.com/shinzey/aggbug/368251.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/shinzey/" target="_blank">蜀山兆孨龘</a> 2012-01-10 17:39 <a href="http://www.tkk7.com/shinzey/archive/2012/01/10/368251.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java |络~程从菜鸟到叫兽 2QTCP 和套接字入门http://www.tkk7.com/shinzey/archive/2012/01/04/367846.html蜀山兆孨龘蜀山兆孨龘Wed, 04 Jan 2012 14:21:00 GMThttp://www.tkk7.com/shinzey/archive/2012/01/04/367846.htmlhttp://www.tkk7.com/shinzey/comments/367846.htmlhttp://www.tkk7.com/shinzey/archive/2012/01/04/367846.html#Feedback5http://www.tkk7.com/shinzey/comments/commentRss/367846.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/367846.html

JDK 提供了对 TCPQTransmission Control ProtocolQ传输控制协议)?UDPQUser Datagram ProtocolQ用h据报协议Q这两个数据传输协议的支持。本文开始探?TCP?/p>

TCP 基础知识

在“服务器-客户端”这U架构中Q服务器和客L各自l护一个端点,两个端点需要通过|络q行数据交换。TCP U需求提供了一U可靠的式q接Q流式的意思是传出和收到的数据都是q箋的字节,没有Ҏ据量q行大小限制。一个端点由 IP 地址和端口构成(专业术语为“元l?{IP 地址, 端口}”)。这P一个连接就可以由元l?{本地地址, 本地端口, q程地址, q程端口} 来表C?/p>

q接q程

?TCP ~程接口中,端点体现?TCP 套接字。共有两U?TCP 套接字:d和被动,“被动”状态也常被UCؓ“侦听”状态。服务器和客L利用套接字进行连接的q程如下Q?/p>

  1. 服务器创Z个被动套接字Q开始@环侦听客L的连接?/li>
  2. 客户端创Z个主动套接字Q连接服务器?/li>
  3. 服务器接受客L的连接,q创Z个代表该q接的主动套接字?/li>
  4. 服务器和客户端通过步骤 2 ?3 中创建的两个d套接字进行数据传输?/li>

下面是连接过E的图解Q?/p>

TCP q接
TCP q接

一个简单的 TCP 服务?/h1>

JDK 提供?ServerSocket cL代表 TCP 服务器的被动套接字。下面的代码演示了一个简单的 TCP 服务器(多线E阻塞模式)Q它不断侦听q接受客L的连接,然后客L发送过来的文本按行dQ全文{换ؓ大写后返回给客户端,直到客户端发送文本行 byeQ?/p>

public class TcpServer implements Runnable {
    private ServerSocket serverSocket;

    public TcpServer(int port) throws IOException {
        // 创徏l定到某个端口的 TCP 服务器被动套接字?
        serverSocket = new ServerSocket(port);
    }

    @Override
    public void run() {
        while (true) {
            try {
                // 以阻塞的方式接受一个客Lq接Q返回代表该q接的主动套接字?
                Socket socket = serverSocket.accept();
                // 在新U程中处理客Lq接?
                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) {  // 减少代码量的花招…?
            // 包装套接字的输入以d客户端发送的文本行?
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    s.getInputStream(), StandardCharsets.UTF_8));
            // 包装套接字的输出以向客L发送{换结果?
            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;
                }

                // {换结果输出给客户端?
                out.println(line.toUpperCase(Locale.ENGLISH));
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}
    

d模式的编E方式简单,但存在性能问题Q因为服务器U程会卡d接受客户端的 accept() Ҏ上,不能有效利用资源。套接字支持非阻塞模式,现在暂时略过?/p>

一个简单的 TCP 客户?/h1>

JDK 提供?Socket cL代表 TCP 客户端的d套接字。下面的代码演示了上q服务器的客LQ?/p>

public class TcpClient implements Runnable {
    private Socket socket;

    public TcpClient(String host, int port) throws IOException {
        // 创徏q接到服务器的套接字?
        socket = new Socket(host, port);
    }

    @Override
    public void run() {
        try (Socket s = socket) {  // 再次减少代码量…?
            // 包装套接字的输出以向服务器发送文本行?
            PrintWriter out = new PrintWriter(new OutputStreamWriter(
                    s.getOutputStream(), StandardCharsets.UTF_8), true);
            // 包装套接字的输入以d服务器返回的文本行?
            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;
                }

                // 文本行发送给服务器?
                out.println(line);
                // 打印服务器返回的文本行?
                console.writer().println(in.readLine());
            }

            // 通知服务器关闭连接?
            out.println("bye");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}
    

?JDK 文档可以看到Q?code>ServerSocket ?Socket 在初始化的时候,可以讑֮一些参敎ͼq支持gq绑定。这些东西对性能和行为都有所影响。下一文章将详解q两个类的初始化?/p>

蜀山兆孨龘 2012-01-04 22:21 发表评论
]]>Java |络~程从菜鸟到叫兽 1QIPhttp://www.tkk7.com/shinzey/archive/2011/12/30/367593.html蜀山兆孨龘蜀山兆孨龘Fri, 30 Dec 2011 09:39:00 GMThttp://www.tkk7.com/shinzey/archive/2011/12/30/367593.htmlhttp://www.tkk7.com/shinzey/comments/367593.htmlhttp://www.tkk7.com/shinzey/archive/2011/12/30/367593.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/367593.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/367593.html

我竟然到现在才发现《Fundamental Networking in Java》这本神作,真有Ҏ地自容的感觉。最q几q做的都是所谓的企业U开发,免不了和|络打交道,但在实际工作中,往往会采用框架将底层l节和上层应用隔dQ感觉就像是在一?Word 模板表单里面填写内容Q做出来也没什么成感。虽然没有不使用框架的理由,但我q真是有Ҏ念当初直接用套接字做|络~程的日子,既能掌控更多东西Q还可以学到更多知识Qؓ研究框架的实现原理打基础。闲话完毕,转入今天的正题:IPQInternet ProtocolQ互联网协议Q?/p>

IP 基础知识

说到 IPQ大多数人的W一反应估计都是 IP 地址。其?IP 是一U协议,IP 地址只是协议的一部分。《RFC 791 - INTERNET PROTOCOL》说Q“互联网协议是ؓ在包交换计算机通信|络的互联系l中使用而设计的。”IP 包含三方面的功能Q?/p>

  1. 用于查找L的寻址pȝ
  2. 包格式的定义
  3. 传输和接收包的规?/li>

IP 的相?Java c?/h1>

?Java 的角度来看上面说到的三个功能Q只有第一个是开发h员需要关心的。另外两个都依赖底层pȝ的实玎ͼJDK 也没有提供相关的cd操作。下面一一介绍 JDK 提供的用于处?IP 地址的类?/p>

InetAddress

此类用来表示 IP 地址Q它有两个子c:Inet4Address ?Inet6AddressQ分别用于处?IPv4 ?IPv6 两个版本。在实际应用中,InetAddress 以应付l大多数情况。它提供了一些静态方法来构造实例,能根据参数格式自动识?IP 版本Q?/p>

public static InetAddress[] getAllByName(String host) throws UnknownHostException
解析指定的主机地址Qƈq回其所有的 IP 地址Q如果传?IP 地址字符Ԍ则只会校验格式,q回的数l也只包含一个代表该 IP 地址的实例。例如,想看看谷歌有多少马甲的话Q?code>InetAddress.getAllByName("www.google.com") 可以了?/dd>
public static InetAddress getByAddress(byte[] addr) throws UnknownHostException
用表C?IP 地址的字节数l(专业术语UCؓ“原?IP 地址”)构造一个实例。IPv4 地址必须?4 个字节,IPv6 必须 16 个。不常用?/dd>
public static InetAddress getByAddress(String host, byte[] addr) throws UnknownHostException
用主机地址和原?IP 地址构造一个实例。此Ҏ应该慎用Q因为它不会对主机名q行解析。即使主机名?IP 地址字符Ԍ也不会检查是否与字节数组一致?/dd>
public static InetAddress getByName(String host) throws UnknownHostException
用主机地址构造一个实例,也可以直接传?IP 地址字符Ԍ{同?getAllByName(host)[0]?/dd>
public static InetAddress getLocalHost() throws UnknownHostException
q回本机在网l中的地址?/dd>
public static InetAddress getLoopbackAddress()
q回环回地址 127.0.0.1Q不抛出异常Q等同于 getByName("localhost")Q不要和 getLocalHost() 搞؜Q。环回地址使主够自p接自己,常被用来对在同一台机器上试|络应用E序。在 IPv4 中,环回地址的网Dؓ 127.0.0.0/8Q通常?127.0.0.1QIPv6 中只有一?::1?/dd>

接下来看?InetAddress 中定义的部分实例ҎQ?/p>

public byte[] getAddress()
q回原始 IP 地址?/dd>
public String getCanonicalHostName()
q回全限定域名。这个方法可以用来探查实际的L名,例如 InetAddress.getByName("www.google.com").getCanonicalHostName() q回 we-in-f99.1e100.net?/dd>
public String getHostAddress()
q回构造时传入的主机地址?/dd>
public String getHostName()
q回L名。如果构造时传入的主机地址?IP 地址字符Ԍ则调?getCanonicalHostName()Q否则直接返回构造时传入的主机地址?/dd>
public boolean isAnyLocalAddress()
查是否ؓ通配W地址。通配W地址?0.0.0.0QIPv4Q或 ::0QIPv6Q,代表所有的本地 IP 地址。例如,假设电脑有两块网卡,各有一个地址Q如果想让一个程序同时监听这两个地址Q就需要用通配W地址?/dd>
public boolean isLinkLocalAddress()
查是否ؓ链\本地地址。IPv4 里定义ؓ地址D?169.254.0.0/16QIpv6 里是?fe80::/64 为前~的地址。在电脑没联|的时候查看本?IPQ就能看到这U地址?/dd>
public boolean isLoopbackAddress()
查是否ؓ环回地址?/dd>
public boolean isSiteLocalAddress()
查是否ؓ站点本地地址。站Ҏ地地址q个名词实际上已l过时了Q现在叫唯一本地地址。IPv4 中未定义QIPv6 中定义ؓ地址D?fc00::/7。这些地址用于U有|络Q例如企业内部的局域网?/dd>

此外q有一些有兛_播地址的方法,暂时略过?/p>

JDK 默认同时支持 IPv4 ?IPv6。如果只想用一U,可以Ҏ情况?java.net.preferIPv4Stack ?java.net.preferIPv6Addresses q两个系l属性之一设ؓ true。两个属性的默认值都?false。一般来说不需要去惊动它们?/p>

SocketAddress

该类是一个空壻I事实上应用程序用的是它的唯一子类 InetSocketAddressQ目前还看不栯计有什么意义。该cd不过?InetAddress 的基上增加了一个端口属性?/p>

NetworkInterface

该类代表|络接口Q例如一块网卡。一个网l接口可以绑定一?IP 地址。具有多个网l接口的L被称为多宿主L。下面的代码可打印出所有本机网l接口的信息Q?/p>

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

在我的笔记本上运行结果ؓQ?/p>

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)


蜀山兆孨龘 2011-12-30 17:39 发表评论
]]>
非主ƈ发工具之 Exchangerhttp://www.tkk7.com/shinzey/archive/2011/12/27/367313.html蜀山兆孨龘蜀山兆孨龘Tue, 27 Dec 2011 02:50:00 GMThttp://www.tkk7.com/shinzey/archive/2011/12/27/367313.htmlhttp://www.tkk7.com/shinzey/comments/367313.htmlhttp://www.tkk7.com/shinzey/archive/2011/12/27/367313.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/367313.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/367313.html

Exchanger 用来让两个线E互相等待ƈ交换计算l果。这个类的用法很单,因ؓ它就定义了两个重载的 exchange ҎQ参数多的那个无非增加了对超时的支持。当一个线E调?exchange 的时候(以计结果作为参敎ͼQ它开始等待另一个线E调?exchangeQ然后两个线E分别收到对方调?exchange 时传入的参数Q从而完成了计算l果的交换?/p>

不用太多的解释,q行下面q个例子׃清二楚:

final Exchanger<String> e = new Exchanger<>();

new Thread() {
    @Override
    public void run() {
        long id = Thread.currentThread().getId();
        String s = "abc";
        System.out.println("U程 [" + id + "] 出 " + s);

        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
            System.out.println("U程 [" + 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("U程 [" + id + "] 出 " + s);

        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
            System.out.println("U程 [" + id + "] 收到 " + e.exchange(s));
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}.start();
    

q行l果Q可能ؓQ:

U程 [9] 出 abc
U程 [10] 出 xyz
U程 [10] 收到 abc
U程 [9] 收到 xyz

最后强调下Q该cd适用于两个线E,妄图用它来处理多个生产者和消费者之间的数据交换是注定要p|的…?/p>

蜀山兆孨龘 2011-12-27 10:50 发表评论
]]>
Z NIO2 ?ZIP 文gpȝhttp://www.tkk7.com/shinzey/archive/2011/12/01/365280.html蜀山兆孨龘蜀山兆孨龘Thu, 01 Dec 2011 05:12:00 GMThttp://www.tkk7.com/shinzey/archive/2011/12/01/365280.htmlhttp://www.tkk7.com/shinzey/comments/365280.htmlhttp://www.tkk7.com/shinzey/archive/2011/12/01/365280.html#Feedback1http://www.tkk7.com/shinzey/comments/commentRss/365280.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/365280.html

以前我曾用两个类Q?a >ZipItem ?ZipSystemQ实C一个简单的 ZIP 文gpȝQ以下简U?ZFSQ。其实这两个类挺好用的Q而且支持嵌套?ZIP 文gQ但是,但是……JDK 7 丢下来一枚叫?NIO2 的笑气炸弹,引入了一套标准的文gpȝ APIQ我承认我中弹了Q手痒了Q又Ҏq套 API 重新实现?ZIP 文gpȝQ终于在今天初步完工Q哈?/p>

话说QJDK 7 其实捆绑销售了一?ZFSQdemo 目录下还有源代码。可……它达不到我的奢求,而且 BUG 不少。随侉K两个:

        // com.sun.nio.zipfs.ZipFileSystemProvider cM的方?
        @Override
        public Path getPath(URI uri) {

            String spec = uri.getSchemeSpecificPart();
            int sep = spec.indexOf("!/");
            if (sep == -1)
                throw new IllegalArgumentException("URI: "
                    + uri
                    + " does not contain path info ex. jar:file:/c:/foo.zip!/BAR");
            // 难怪该Ҏ始终?IllegalArgumentException 异常Q原来你子把文件的 URI
            // 当成 ZFS ?URI 在用…?
            return getFileSystem(uri).getPath(spec.substring(sep + 1));
        }

        // com.sun.nio.zipfs.ZipFileSystem cM的方?
        @Override
        public PathMatcher getPathMatcher(String syntaxAndInput) {
            int pos = syntaxAndInput.indexOf(':');
            // 丫的Qpos == syntaxAndInput.length()Q!谁写的?抓出来鞭?
            if (pos <= 0 || pos == syntaxAndInput.length()) {
                throw new IllegalArgumentException();
    

很明显,官方 ZFS 没有l过代码审阅、没有经q测试、没有经q……然后,@author Xueming ShenQ真是丢咱华夏民族的脸…?/p>

下面列个表格详细比较官方 ZFS 和山?ZFSQ?/p>
比较内容 官方 ZFS 山寨 ZFS
实现方式 另v炉灶Q用U?Java 重新实现了对 ZIP 文g格式的处理代码?/td> Z ZipFile ?ZipInputStream q两个已l稳定多q的c,但涉及了大量本地代码调用Q也怼影响性能?/td>
L?/td> 支持Q且通过解压C时文件支持随问?/td> 支持Q但不支持随问?/td>
写操?/td> 通过解压C时文件进行支持,但无法检到其他q程对同一?ZIP 文g的写操作Q不适用于ƈ发环境?/td> 不支持。ZIP 文g事实上是一个整体,对内部条目的M修改都可能导致重构整个文Ӟ因此所谓的写操作必通过临时文g来处理,效率低下Q意义不大,而且难以处理嵌套 ZIP 文g。这也符合我的原则:不解压?/td>
嵌套 ZIP 文g 不支持?/td> 支持Q当然读取嵌?ZIP 文g会慢一些?/td>
反斜U分隔符 不支持,直接瓜掉?/td> 支持Q且和标准的斜线分隔W区别对待。例如,/abc/ ?/abc\ 不同的文gQ实际上q两个能够ƈ存于 ZIP 文g中?/td>
I目录名 不支持,直接瓜掉?/td> 支持。例?/a/b ?/a//b 是两个可以ƈ存且不同的文件?/td>

山寨 ZFS 的用法示例:

        Map<String, Object> env = new HashMap<>();
        // 用于解码 ZIP 条目名。默认ؓ Charset.defaultCharset()?
        env.put("charset", StandardCharsets.UTF_8);
        // 指示是否自动探测嵌套?ZIP 文g。默认ؓ false?
        env.put("autoDetect", true);
        // 默认目录Q用于创建和解析相对路径。默认ؓ?”?
        env.put("defaultDirectory", "/dir/");

        // 从文件创Z?ZFS?
        try (FileSystem zfs = FileSystems.newFileSystem(
                URI.create("zip:" + Paths.get("docs.zip").toUri()), env)) {
            Path path = zfs.getPath("app.jar");
            if ((Boolean) Files.getAttribute(path, "isZip")) {
                // 创徏一个嵌套的 ZFS?
                try (FileSystem nestedZfs = zfs.provider().newFileSystem(path, env)) {
                    // 此处省略若干行?
                }
            }
        }
    

最后双手奉上源代码Q?a >LL处!



蜀山兆孨龘 2011-12-01 13:12 发表评论
]]>
ȝq算W的一?BUGhttp://www.tkk7.com/shinzey/archive/2011/11/11/363493.html蜀山兆孨龘蜀山兆孨龘Fri, 11 Nov 2011 03:06:00 GMThttp://www.tkk7.com/shinzey/archive/2011/11/11/363493.htmlhttp://www.tkk7.com/shinzey/comments/363493.htmlhttp://www.tkk7.com/shinzey/archive/2011/11/11/363493.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/363493.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/363493.html

先看出错的代码:

        public class Holder<T> {
            private T value;

            public Holder() {
            }

            public Holder(T value) {
                this.value = value;
            }

            public void setValue(T value) {
                this.value = value;
            }

            // 此处省略若干行?
        }

        Holder<Object> holder = new Holder<>("xxx");
    

看v来还好,但编译的时候却报错Q?/p>

Uncompilable source code - incompatible types
  required: zhyi.test.Holder<java.lang.Object>
  found:    zhyi.test.Holder<java.lang.String>

老老实实把cd写出来就没问题:

        Holder<Object> holder = new Holder<Object>("xxx");
    

如果非要用钻矌符的话Q可以采取下列两U方式之一Q?/p>

        // 使用默认构造器Q再调用 setValue Ҏ?
        Holder<Object> holder = new Holder<>();
        holder.setValue("xxx");

        // 使用泛型通配W,但之后就不能调用 setValue 了,否则~译出错?
        Holder<? extends Object> holder = new Holder<>("xxx");
    


蜀山兆孨龘 2011-11-11 11:06 发表评论
]]>
非主ƈ发工具之 CyclicBarrierhttp://www.tkk7.com/shinzey/archive/2011/10/17/361408.html蜀山兆孨龘蜀山兆孨龘Mon, 17 Oct 2011 03:21:00 GMThttp://www.tkk7.com/shinzey/archive/2011/10/17/361408.htmlhttp://www.tkk7.com/shinzey/comments/361408.htmlhttp://www.tkk7.com/shinzey/archive/2011/10/17/361408.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/361408.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/361408.html

CyclicBarrier 的功能类g前面说到?CountDownLatchQ用于让多个U程Q子dQ互相等待,直到共同到达公共屏障点(common barrier pointQ,在这个点上,所有的子Q务都已完成,从而主d完成?/p>

该类构造的时候除了必要指定U程数量Q还可以传入一?Runnable 对象Q它?run Ҏ在到达公共屏障点后执行一ơ。子U程完成计算后,分别调用 CyclicBarrier#await Ҏq入d状态,直到其他所有子U程都调用了 await?/p>

下面仍然以运动员准备赛跑Z来说?CyclicBarrier 的用法:

            final int count = 8;
            System.out.println("q动员开始就位?);

            final CyclicBarrier cb = new CyclicBarrier(count, new Runnable() {
                @Override
                public void run() {
                    System.out.println("比赛开?..");
                }
            });

            for (int i = 1; i <= count; i++) {
                final int number = i;
                new Thread() {
                    @Override
                    public void run() {
                        System.out.println(number + " 可动员到场q开始准?..");
                        try {
                            // 准备 2~5 U钟?
                            TimeUnit.SECONDS.sleep(new Random().nextInt(4) + 2);
                        } catch (InterruptedException ex) {
                        }
                        System.out.println(number + " 可动员׃?);
                        try {
                            cb.await();
                        } catch (InterruptedException | BrokenBarrierException ex) {
                        }
                    }
                }.start();
            }
            System.out.println("{待所有运动员׃...");
    

q行输出Q可能)为:

q动员开始就位?
1 可动员到场q开始准?..
2 可动员到场q开始准?..
{待所有运动员׃...
3 可动员到场q开始准?..
4 可动员到场q开始准?..
6 可动员到场q开始准?..
8 可动员到场q开始准?..
5 可动员到场q开始准?..
7 可动员到场q开始准?..
1 可动员׃?
3 可动员׃?
8 可动员׃?
6 可动员׃?
2 可动员׃?
7 可动员׃?
5 可动员׃?
4 可动员׃?
比赛开?..

最后看?CyclicBarrier ?CountDownLatch 的主要异同:

  1. 两者在构造的时候都必须指定U程数量Q而且该数量在构造后不可修改?/li>
  2. 前者可以传入一?Runnable 对象Q在d完成后自动调用,执行者ؓ某个子线E;后者可?await Ҏ后手动执行一D代码实现相同的功能Q但执行者ؓȝE?/li>
  3. 前者在每个子线E上都进行阻塞,然后一h行;后者仅在主U程上阻塞一ơ?/li>
  4. 前者可以重复用;后者的倒计数器归零后就作废了?/li>
  5. 两者的内部实现完全不同?/li>


      蜀山兆孨龘 2011-10-17 11:21 发表评论
      ]]>非主ƈ发工具之 CountDownLatchhttp://www.tkk7.com/shinzey/archive/2011/10/14/361277.html蜀山兆孨龘蜀山兆孨龘Fri, 14 Oct 2011 06:22:00 GMThttp://www.tkk7.com/shinzey/archive/2011/10/14/361277.htmlhttp://www.tkk7.com/shinzey/comments/361277.htmlhttp://www.tkk7.com/shinzey/archive/2011/10/14/361277.html#Feedback1http://www.tkk7.com/shinzey/comments/commentRss/361277.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/361277.html

      ֐思义Q?code>CountDownLatch 是一个用来倒计数的咚咚。如果某Q务可以拆分成若干个子d同时q行Q然后等待所有的子Q务完成,可以考虑使用它?/p>

      该类的用法非常简单。首先构造一?CountDownLatchQ唯一的参数是d数量Q一旦构造完毕就不能修改。接着启动所有的子Q务(U程Q,且每个子d在完成自q计算后,调用 CountDownLatch#countDown Ҏ倒计数减一。最后在ȝE中调用 CountDownLatch#await Ҏ{待计数器归零?/p>

      例如赛跑的准备阶D,八名q动员先后到达v点做好准备,然后裁判打响发o枪,准备工作q束了Q比赛开始。如果把从运动员׃到发令枪响看做赛跑准备Q务,那么每个q动员的准备q程是其子dQ可以用 CountDownLatch 模拟如下Q?/p>

              final int count = 8;
              System.out.println("q动员开始就位?);
      
              // 构?CountDownLatch?
              final CountDownLatch cdl = new CountDownLatch(count);
              for (int i = 1; i <= count; i++) {
                  final int number = i;
                  new Thread() {
                      @Override
                      public void run() {
                          System.out.println(number + " 可动员到场q开始准?..");
                          try {
                              // 让运动员随机准备 2~5 U钟?
                              TimeUnit.SECONDS.sleep(new Random().nextInt(4) + 2);
                          } catch (InterruptedException ex) {
                          }
                          System.out.println(number + " 可动员׃?);
                          // 倒计数减一?
                          cdl.countDown();
                      }
                  }.start();
              }
      
              System.out.println("{待所有运动员׃...");
              try {
                  // {待倒计数变?0?
                  cdl.await();
                  System.out.println("比赛开始?);
              } catch (InterruptedException ex) {
              }
          

      q行输出Q可能)为:

      q动员开始就位?
      1 可动员到场q开始准?..
      2 可动员到场q开始准?..
      4 可动员到场q开始准?..
      {待所有运动员׃...
      8 可动员到场q开始准?..
      6 可动员到场q开始准?..
      3 可动员到场q开始准?..
      7 可动员到场q开始准?..
      5 可动员到场q开始准?..
      6 可动员׃?
      1 可动员׃?
      5 可动员׃?
      4 可动员׃?
      7 可动员׃?
      8 可动员׃?
      2 可动员׃?
      3 可动员׃?
      比赛开始?/pre>
          

      从上面的例子q可以看?CountDownLatch 的局限性和 CompletionService cMQ在于无法处理子d数量不确定的情况Q例如统计某个文件夹中的文g数量。另外,如果某个子Q务在调用 countDown 之前挂掉了Q倒计数就永远不会归零。对于这U情况,要么?finally 之类的手D保?countDown 一定会被调用,要么用带参数?await Ҏ指定时旉?/p>

      蜀山兆孨龘 2011-10-14 14:22 发表评论
      ]]>非主ƈ发工具之 CompletionServicehttp://www.tkk7.com/shinzey/archive/2011/09/29/359781.html蜀山兆孨龘蜀山兆孨龘Thu, 29 Sep 2011 05:37:00 GMThttp://www.tkk7.com/shinzey/archive/2011/09/29/359781.htmlhttp://www.tkk7.com/shinzey/comments/359781.htmlhttp://www.tkk7.com/shinzey/archive/2011/09/29/359781.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/359781.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/359781.html

      CompletionService 接口的实例可以充当生产者和消费者的中间处理引擎Q从而达到将提交d和处理结果的代码q行解耦的目的。生产者调?submit Ҏ提交dQ而消费者调?pollQ非dQ或 takeQ阻塞)Ҏ获取下一个结果:q一特征看v来和d队列Q?code>BlockingQueueQ类|两者的区别在于 CompletionService 要负责Q务的处理Q而阻塞队列则不会?/p>

      ?JDK 中,该接口只有一个实现类 ExecutorCompletionServiceQ该cM用创建时提供?Executor 对象Q通常是线E池Q来执行dQ然后将l果攑օ一个阻塞队列中Q果然本是一家亲啊!ExecutorCompletionService 线E池和阻塞队列糅合在一P仅仅通过三个ҎQ就实现了Q务的异步处理Q可谓ƈ发编E初学者的兵利器Q?/p>

      接下来看一个例子。楼L一大堆 *.java 文gQ需要计它们的代码总行数。利?ExecutorCompletionService 可以写出很简单的多线E处理代码:

              public int countLines(List<Path> javaFiles) throws Exception {
                  // Ҏ处理器数量创建线E池。虽然多U程q不保证能够提升性能Q但适量?
                  // 开U程一般可以从pȝ骗取更多资源?
                  ExecutorService es = Executors.newFixedThreadPool(
                          Runtime.getRuntime().availableProcessors() * 2);
                  // 使用 ExecutorCompletionService 内徏的阻塞队列?
                  CompletionService cs = new ExecutorCompletionService(es);
      
                  // 按文件向 CompletionService 提交d?
                  for (final Path javaFile : javaFiles) {
                      cs.submit(new Callable<Integer>() {
                          @Override
                          public Integer call() throws Exception {
                              // 略去计算单个文g行数的代码?
                              return countLines(javaFile);
                          }
                      });
                  }
      
                  try {
                      int loc = 0;
                      int size = javaFiles.size();
                      for (int i = 0; i < size; i++) {
                          // take Ҏ{待下一个结果ƈq回 Future 对象。不直接q回计算l果是ؓ?
                          // 捕获计算时可能抛出的异常?
                          // poll 不等待,有结果就q回一?Future 对象Q否则返?null?
                          loc += cs.take().get();
                      }
                      return loc;
                  } finally {
                      // 关闭U程池。也可以线E池提升为字D以侉K用?
                      // 如果dU程QCallable#callQ能响应中断Q用 shutdownNow 更好?
                      es.shutdown();
                  }
              }
          

      最后,CompletionService 也不是到处都能用Q它不适合处理d数量有限但个C可知的场景。例如,要统计某个文件夹中的文g个数Q在遍历子文件夹的时候也会“递归地”提交新的Q务,但最后到底提交了多少Q以及在什么时候提交完了所有Q务,都是未知敎ͼ无论 CompletionService q是U程池都无法q行判断。这U情况只能直接用U程池来处理?/p>

      蜀山兆孨龘 2011-09-29 13:37 发表评论
      ]]>
      վ֩ģ壺 ޹Ʒyw| ҹƵ| ҹδʮ| ɫͼ߹ۿ| Ѱββ8X | þþƷ޾Ʒɫ| ںӰԺ߹ۿƵ | ŷ޾Ʒ| þùƷһ| AVһ߹ۿ| һվ | þһѲ| vaֻߵӰ| һ| avһ| þóѴƬ| վѹۿ| Ӱȷѹۿ| ߹ۿ޾Ʒר| Ļ˿Ʒһ| պѸ岥| ޾Ʒþþþþþ| ߹ۿվ| ۺһۺͼ| ޹˾þһWWW| ƷƬѿ| AVһAV| ѻɫַ| ۺС˵ɫ| վѹۿ| xxxxx| Ʒ97˳Ƶ| 컶ëƬ | ƷŮ߹ۿ| һȫٸɫƬ| ľþþƷ| jyzzjyzz鶹| ƷƵ| ޾Ʒһ߾þ| 99ƵѹۿƵ| ձƷþþþþþþ|