Java NIO 类库Selector机制解析(续)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介:
在前些天的《 Java NIO类库Selector机制解析 》文章中,我们知道了下面的事情:
 
1)SunJVM在实现Selector上,在LinuxWindows平台下的细节。
2)Selector类的wakeup()方法如何唤醒阻塞在select()系统调用上的细节。
 
先给大家做一个简单的回顾,在Windows下,SunJava虚拟机在Selector.open()时会自己和自己建立loopbackTCP链接;在Linux下,Selector会创建pipe。这主要是为了Selector.wakeup()可以方便唤醒阻塞在select()系统调用上的线程(通过向自己所建立的TCP链接和管道上随便写点什么就可以唤醒阻塞线程)
 
我们知道,无论是建立TCP链接还是建立管道都会消耗系统资源,而在Windows上,某些Windows上的防火墙设置还可能会导致JavaSelector因为建立不起loopbackTCP链接而出现异常。
 
而在我的另一篇文章《 GDB调试Java程序 》中介绍了另一个Java的解释器——GNUgij,以及编译器gcj,不但可以比较高效地运行Java程序,而且还可以把Java程序直接编译成可执行文件。
 
GNU的之所以要重做一个Java的编译和解释器,其一个重要原因就是想解释SunJVM的效率和资源耗费问题。当然,GNUJava编译/解释器并不需要考虑太多复杂的平台,他们只需要专注于Linux和衍生自Unix System V的操作系统,对于开发人员来说,离开了Windows,一切都会变得简单起来。在这里,让我们看看GNUgij是如何解释Selector.open()Selector.wakeup()的。
 
同样,我们需要一个测试程序。在这里,为了清晰,我不会例出所有的代码,我只给出我所使用的这个程序的一些关键代码。
 
我的这个测试程序中,和所有的Socket程序一样,下面是一个比较标准的框架,当然,这个框架应该是在一个线程中,也就是一个需要继承Runnable接口,并实现run()方法的一个类。(注意:其中的s是一个成员变量,是Selector类型,以便主线程序使用)
 
 
        // 生成一个侦听端
        ServerSocketChannel ssc = ServerSocketChannel.open();
        // 将侦听端设为异步方式
        ssc.configureBlocking(false);
        // 生成一个信号监视器
        s = Selector.open();
        // 侦听端绑定到一个端口
        ssc.socket().bind(new InetSocketAddress(port));
        // 设置侦听端所选的异步信号 OP_ACCEPT
        ssc.register(s,SelectionKey.OP_ACCEPT);
  
        System.out.println("echo server has been set up ......");
 
        while(true){
            int n = s.select();
            if (n == 0) { // 没有指定的 I/O 事件发生
               continue;
            }    
            Iterator it = s.selectedKeys().iterator();    
            while (it.hasNext()) {
                SelectionKey key = (SelectionKey) it.next();
                if (key.isAcceptable()) { // 侦听端信号触发
                     …… …… ……
                     …… …… ……
                }  
                if (key.isReadable()) { // socket 可读信号
                     …… …… ……
                     …… …… ……                   
                }    
                it.remove();
            }
         }


 
而在主线程中,我们可以通过Selector.wakeup()来唤醒这个阻塞在select()上的线程,下面是写在主线程中的唤醒程序:
 
 
new Thread(this).start();
try{
    //Sleep 30 seconds
    Thread.sleep(30000);
    System.out.println("wakeup the select");
    s.wakeup();
}catch(Exception e){
        e.printStackTrace();
}
 
 
这个程序在主线程中,先启动一个线程,也就是上面那个Socket线程,然后休息30秒,为的是让上面的那个线程有阻塞在select(),然后打印出一条信息,这是为了我们用strace命令查看具体的系统调用时能够快速定位。之后调用的是Selectorwakeup()方法来唤醒侦听线程。
 
接下来,我们可以通过两种方式来编译这个程序:
1)使用gcj或是sunjavac编译成class文件,然后使用gij解释执行。
2)使用gcj直接编译成可执行文件。
(无论你用那种方法,都是一样的结果,本文使用第二种方法,关于gcj的编译方法,请参看我的《 GDB调试Java程序 》)
 
编译成可执行文件后,执行程序时,使用lsof命令,我们可以看到没有任何pipe的建立。可见GNU的解释更为的节省资源。而对于一个UnixC程序员来说,这意味着如果要唤醒select()只能使用pthread_kill()来发送一个信号了。下面就让我们使用strace命令来验证这个想法。
 
下图是使用strace命令来跟踪整个程序运行时的系统调用,我们利用我们的输出的“wakeup the select”字符串快速的找到了wakeup的实际系统调用。
 
 
果然,我们可可以看到,tgkill(5829, 5831, SIGHUP)这个系统调用,第一个参数是“源线程id”,第二个参数是“目的线程id”,第三个参数是“信号SIGHUP”。通过每一行前面的线程号我们可以看到紧接着tgkill后面的5831线程的“… select resumed”字样。
 
可见,GNU的确是使用最为传统的pthread_killkill系统调用向阻塞线程发信号的方法来实现Selector.wakeup()的,这也证明了GNUJava编译/解释器是不会消耗系统文件描述符的。而我们也终于看到了回归经典的Java实现机制。









本文转自 haoel 51CTO博客,原文链接:http://blog.51cto.com/haoel/124570,如需转载请自行联系原作者

目录
相关文章
|
7天前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
1天前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
22 6
|
1天前
|
Java 编译器 数据库连接
Java中的异常处理机制深度解析####
本文深入探讨了Java编程语言中异常处理机制的核心原理、类型及其最佳实践,旨在帮助开发者更好地理解和应用这一关键特性。通过实例分析,揭示了try-catch-finally结构的重要性,以及如何利用自定义异常提升代码的健壮性和可读性。文章还讨论了异常处理在大型项目中的最佳实践,为提高软件质量提供指导。 ####
|
5天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
5天前
|
存储 分布式计算 Java
存算分离与计算向数据移动:深度解析与Java实现
【11月更文挑战第10天】随着大数据时代的到来,数据量的激增给传统的数据处理架构带来了巨大的挑战。传统的“存算一体”架构,即计算资源与存储资源紧密耦合,在处理海量数据时逐渐显露出其局限性。为了应对这些挑战,存算分离(Disaggregated Storage and Compute Architecture)和计算向数据移动(Compute Moves to Data)两种架构应运而生,成为大数据处理领域的热门技术。
21 2
|
5天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。
|
5天前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
11天前
|
存储 缓存 安全
🌟Java零基础:深入解析Java序列化机制
【10月更文挑战第20天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
20 3
|
10天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
25 1
|
4天前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
7 0

推荐镜像

更多
下一篇
无影云桌面