Thread, Process 线程与进程

线程 进程在各自独立的空间中运行,进程之间共享数据需要用 mmap 或者进程间通信机制 (IPC) 如何在一个进程空间中执行多个线程,有些情况需要在一个进程中同时执行多个控制流程,这时候线程就派上了用场, 线程是比进程更轻量级的调度执行单位,线程的引入可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源(内存地址,文件IO等),又可以独立调度 (线程是CPU调度的基本单位) 。 线程共享以下进程的资源和环境 文件描述符表 (重点) 每种信号的处理方式 (SIG_IGN、SIG_DFL或者自定义的信号处理函数) 当前工作目录 用户id和组id 线程有自己的私有数据 线程id 上下文信息, 包括各种寄存器的值, 程序计数器, 栈指针 栈空间 (临时变量存储在栈空间中) (重点) errno变量 信号屏蔽字 调度优先级 Linux上线程位于libpthread共享库中,因此在编译时要加上-lpthread选项 (-l :指明所链接的库) 进程与线程的联系与区别 线程是在进程内部运行的执行分支 线程是为了资源共享 (共享地址空间) ,进程是为了资源独占 (私有地址空间) Linux下没有真正的线程,它是利用轻量级进程来代替实现的 进程是分配资源 (资源管理) 的最小单元;而线程是调度资源 (程序执行) 的最小单元 线程与线程之间是独立的 实现线程主要有三种方式: 使用内核线程实现,使用用户线程实现和使用用户线程加轻量级进程混合实现。 java thread Thread Dump日志的线程信息 "resin-22129" daemon prio=10 tid=0x00007fbe5c34e000 nid=0x4cb1 waiting on condition [0x00007fbe4ff7c000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:315) at com.caucho.env.thread2.ResinThread2.park(ResinThread2.java:196) at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:147) at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118) "Timer-20" daemon prio=10 tid=0x00007fe3a4bfb800 nid=0x1a31 in Object.wait() [0x00007fe3a077a000] java.lang.Thread.State: TIMED_WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000006f0620ff0> (a java.util.TaskQueue) at java.util.TimerThread.mainLoop(Timer.java:552) - locked <0x00000006f0620ff0> (a java.util.TaskQueue) at java.util.TimerThread.run(Timer.java:505) 以上依次是: ...

Created 30306-06-30 · Updated 9096-10-09 · 6 min · 1128 words · -

IP, TCP 和 HTTP

IP,TCP 和 HTTP 172.16.128.0/24 经常看到这类IP地址"/24"表示什么意思? 子网掩码, 代表多少个1 子网掩码是4个8位2进制数组成的, 换化成10进制是就是现在这样的, 如255.255.255.0, 换成2进制的话就是11111111 11111111 11111111 00000000, 前面有24个1, 也就是ip/24, 192.168.1.1/24 就表示ip是192.168.1.1, 子网掩码是255.255.255.0 http://objccn.io/issue-10-6/ 当 app 和服务器进行通信的时候,大多数情况下,都是采用 HTTP 协议。HTTP 最初是为 web 浏览器而定制的,如果在浏览器里输入 http://www.objc.io ,浏览器会通过 HTTP 协议和 www.objc.io 所对应的服务器进行通信。 HTTP是运行在应用层上的应用协议,而不同的层级上都有相应的协议在运行。层级的堆栈关系一般可以这么描述: Application Layer - e.g. HTTP – Transport Layer - e.g. TCP – Internet Layer - e.g. IP – Link Layer - e.g. IEEE 802.2 所谓的 OSI (Open Systems Interconnection,开放式系统互联) 模型定义了七层结构。本文会关注应用层 (application layer)、传输层 (transport layer) 和网络层 (internet layer),它们分别代表了典型的 HTTP 的应用的 HTTP,TCP 以及 IP。在 IP 之下的是数据连接和物理层级,比如像 Ethernet 的实现之类的东西 (Ethernet 拥有一个数据连接部分以及一个物理部分) 。 ...

Created 29296-07-29 · Updated 9096-10-09 · 11 min · 2254 words · -

Mockito

Mockito 部分mock (partial mock) 部分mock是说一个类的方法有些是实际调用,有些是使用mockito的stubbing (桩实现) 。 为什么需要部分mock 当需要测试一个组合方法 (一个方法需要其它多个方法协作) 的时候,某个叶子方法 (只供别人调用,自己不依赖其它反复) 已经被测试过,我们其实不需要再次测试这个叶子方法,so,让叶子打桩实现返回结果,上层方法实际调用并测试。 mockito实现部分mock的两种方式: spy和 callRealMethod() spy的原理是,如果不打桩默认都会执行真实的方法,如果打桩则返回桩实现。可以看出spy.size()通过桩实现返回了值100,而spy.get(0)则返回了实际值 List<String> list = new LinkedList<String>(); List<String> spy = spy(list); when(spy.size()).thenReturn(100); spy.add("one"); spy.add("two"); assertEquals(spy.get(0), "one"); assertEquals(100, spy.size()); Channel channel = mock(Channel.class); when(channel.writeAndFlush(obj)).thenReturn(null); // 重置 spy 对象,让 add(1,2) 调用真实方法,返回 3 when(exampleService.add(1, 2)).thenCallRealMethod(); Assert.assertEquals(3, exampleService.add(1, 2)); 一、什么是mock测试,什么是mock对象? 一种替代方案就是使用mocks 从图中可以清晰的看出 mock对象就是在调试期间用来作为真实对象的替代品。 mock测试就是在测试过程中,对那些不容易构建的对象用一个虚拟对象来代替测试的方法就叫mock测试。 知道什么是mock测试后,那么我们就来认识一下mock框架—Mockito 二、什么是Mockito 除了有一个好记的名字外,Mockito尝试用不一样的方法做mocking测试,是简单轻量级能够替代EasyMock的框架。使用简单,测试代码可读性高,丰富的文档包含在javadoc中,直接在IDE中可查看文档,实例,说明。更多信息: http://code.google.com/p/mockito/ ...

Created 13136-08-13 · Updated 9096-10-09 · 2 min · 284 words · -

Fastjson

Fastjson // 序列化 String text = JSON.toJSONString(obj); // 反序列化 Map<String, Object> foo = JSON.parseObject(jsonStr0, Map.class); fastjson 对象转换时重命名字段名 @JSONField(name="total_count") private int totalCount; @JSONField(name="incomplete_results") private boolean incompleteResults = false; Map<String, Object> userMap = JSON.parseObject(o, new TypeReference<Map<String, Object>>() {}); 使用Fastjson序列化与反序列化对象 public class JSONobject { private String obj; private String color; public String getObj() { return obj; } public void setObj(String obj) { this.obj = obj; } public String getcolor() { return color; } public void setcolor(String color) { this.color = color; } } public class fastjson { public static void main(String[] args) { // TODO Auto-generated method stub JSONobject ins = new JSONobject(); ins.setColor("red"); ins.setObj("s"); //序列化 String text = JSON.toJSONString(ins); System.out.println(text); //反序列化 JSONobject ins1 = JSON.parseObject(text, JSONobject.class); System.out.println(ins1.getColor()); System.out.println(ins1.getObj()); } } 显示的结果是: ...

Created 13136-08-13 · Updated 9096-10-09 · 2 min · 345 words · -

Maven Release 版本管理

Maven http://juvenshun.iteye.com/blog/376422 什么是版本管理 首先,这里说的版本管理 (version management) 不是指版本控制 (version control) ,但是本文假设你拥有基本的版本控制的知识,了解subversion的基本用法。版本管理中说得版本是指构件 (artifact) 的版本,而非源码的版本 (如subversion中常见的rXXX,或者git中一次提交都有个sha1的commit号) 。 比如我有一个项目,其artifactId为myapp,随着项目的进展,我们会生成这样一些jar: myapp-1.0-SNAPSHOT.jar,myapp-1.0.jar,myapp-1.1-SNAPSHOT.jar,myapp-1.0.1.jar等等。你可能会说,这很简单啊,我在POM中改个version,mvn clean install不就完了?但这只是表面,本文我将讲述,snapshot和release版本的区别,如何自动化版本发布 (如果你的项目有几十个module,你就会觉得手工改POM来升级版本是很痛苦的事情) ,结合自动化发布的过程,这里还会介绍maven-release-plugin。此外,一些scm概念也会被涉及到,比如tag和branch。 前提: 版本控制 不管怎样,我们都需要建立一个项目并提交到SCM中,这里我以subversion为例。你得有一个配置好的subversion repository,这里我建立了一个空的svn仓库,其地址为: https://192.168.1.100:8443/svn/myapp/ 现在,该目录下只有三个空的典型的子目录: /trunk/, branches/, tags/。分别用来存放主干,分支,以及标签。 接着将项目导入到svn仓库中,到项目根目录,运行如下命令: svn import -m 'project initialization' https://192.168.1.100:8443/svn/myapp/trunk (注意,这么做你会将目录下所有文件导入到svn库中,但是这其中某些目录和文件是不应该被导入的,如/target目录,以及eclipse相关的项目文件) 目前,我们将项目的版本设置为1.0-SNAPSHOT。 为什么用SNAPSHOT? 我先说说如果没有SNAPSHOT会是什么样子。假设你的项目有2个模块,A,B,其中A依赖B。这两个模块分别由甲,乙两个个人负责开发。在开发过程中,因为A是依赖于B的,因此乙每次做一个改动都会影响到甲,于是,乙提交了一些更改后,需要让甲看到。这个时候,怎么做呢?乙对甲说,“你签出我的代码,build一下就OK了”,甲有点不情愿,但还是照做了,签出代码,svn clean install,然后,发现build出错了,有个测试没有pass。甲郁闷了,对乙说,“你的代码根本不能用,我不想build,你build好了给我”,乙看了看确实自己的代码build不过,于是回去解决了,然后打了个jar包,扔给甲,甲对了对groupId,artifactId,放到了自己的.m2/repository/目录下,OK,能用了。 于是乙每次更新都这样做,打包,复制,然后甲粘贴,使用……渐渐的,大家发现这是个很笨的办法,这是纯手工劳动阿,程序员最BS的就是重复劳动。一天,甲对乙说,“你知道nexus么?你把你的jar发布到nexus上就可以了,我要用就自动去下载,这多棒!“乙说"哦?有这好东西,我去看看"于是乙发现了nexus这块新大陆,并成功的发布了B到nexus上。 (见,Nexus入门指南, (图文) ) 。 但是,请注意,我们这里的一切都假设没有SNAPSHOT,因此如果乙不更改版本,甲下载一次如B-1.0.jar之后,maven认为它已经有了正确的B的版本,就不会再重新下载。甲发现了这个问题,对乙说"你的更新我看不到,你更新了么?“乙说"不可能!我看看”,于是检查一下甲下载的C-1.0.jar,发现那是几天前的。乙一拍脑袋,说"这简单,我更新一下我的版本就好了,我发布个B-1.1.jar上去,你更新下依赖版本”,甲照做了,似乎这么做是可行的。 这里有一个问题,一次提交就更新一个版本,这明显不是正确的管理办法,此外,乙得不停的通知甲更新对B的依赖版本,累不累阿?1.0,或者说1.1,2.0,都代表了稳定,这样随随便便的改版本,能稳定么? 所以Maven有SNAPSHOT版本的概念,它与release版本对应,后者是指1.0,1.1,2.0这样稳定的发布版本。 现在乙可以将B的版本设置成1.0-SNAPSHOT,每次更改后,都mvn deploy到nexus中,每次deploy,maven都会将SNAPSHOT改成一个当前时间的timestamp,比如B-1.0-SNAPSHOT.jar到nexus中后,会成为这个样子: B-1.0-20081017-020325-13.jar。Maven在处理A中对于B的SNAPSHOT依赖时,会根据这样的timestamp下载最新的jar,默认Maven每天 更新一次,如果你想让Maven强制更新,可以使用-U参数,如: mvn clean install -U 。 现在事情简化成了这个样子: 乙做更改,然后mvn deploy,甲用最简单的maven命令就能得到最新的B。 从1.0-SNAPSHOT到1.0到1.1-SNAPSHOT SNAPSHOT是快照的意思,项目到一个阶段后,就需要发布一个正式的版本 (release版本) 。一次正式的发布需要这样一些工作: 在trunk中,更新pom版本从1.0-SNAPSHOT到1.0 对1.0打一个svn tag 针对tag进行mvn deploy,发布正式版本 更新trunk从1.0到1.1-SNAPSHOT 你可以手工一步步的做这些事情,无非就是一些svn操作,一些pom编辑,还有一些mvn操作。但是你应该明白,手工做这些事情,一来繁琐,而来容易出错。因此这里我介绍使用maven插件来自动化这一系列动作。 SCM ...

Created 14146-08-14 · Updated 9096-10-09 · 2 min · 242 words · -

Java 读取环境变量

Java读 环境变量 http://ling091.iteye.com/blog/354052 读取环境变量时可以使用 System.getProperty 或 System.getenv 方法。 System.getProperty 方法 ( JDK1.4 ) 用来读取针对 JVM 的属性,如程序当前的运行路径、路径分隔符、 Java 版本等, ( 见 System.getProperty() 参数大全 ) ,它也可以读取在运行程序时设置的自定义属性。 获取一个JVM已定义属性 //获取系统当前的运行路径 System.out.println("current path = " + System.getProperty("user.dir") ); 输出: current path = E:\program\java\test\Test 获取应用程序的属性: 在命令中输入下面的命令,其中的-D用于设置一个属性 -D<name>=<value> SET myvar=Hello world SET myothervar=nothing java -Dmyvar="%myvar%" -Dmyothervar="%myothervar%" myClass myClass中读取这些属性 String myvar = System.getProperty("myvar"); String myothervar = System.getProperty("myothervar"); 如果要读取操作系统的环境变量 (如 Path 、 TEMP 或 TMP 、 JAVA_HOME 等。) 则可以使用 System.getenv 方法,但是由于某些原因,该方法被去掉了,直到 JDK1.5 后,该方法又被加进去 [3] 。 ...

Created 14146-08-14 · Updated 9096-10-09 · 1 min · 154 words · -

Maven jar plugin

Maven jar plugin 1.修改pom.xml增加如下内容 <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.4</version> <configuration> <manifest> true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass>com.sysware.HelloWorld</mainClass> </manifest> </archive> </configuration> </plugin> 运行mvn clean package即可 <build> <finalName>...</finalName> <sourceDirectory>src/main/java</sourceDirectory> <resources> <!-- 控制资源文件的拷贝 --> <resource> <directory>src/main/resources</directory> <targetPath>${project.build.directory}</targetPath> </resource> </resources> <plugins> <!-- 设置源文件编码方式 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <defaultLibBundleDir>lib</defaultLibBundleDir> <source>1.6</source> <target>1.6</target> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- 打包jar文件时,配置manifest文件,加入lib包的jar依赖 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass>.....MonitorMain</mainClass> </manifest> </archive> </configuration> </plugin> <!-- 拷贝依赖的jar包到lib目录 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory> ${project.build.directory}/lib </outputDirectory> </configuration> </execution> </executions> </plugin> <!-- 解决资源文件的编码问题 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.3</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- 打包source文件为jar文件 --> <plugin> <artifactId>maven-source-plugin</artifactId> <version>2.1</version> <configuration> <attach>true</attach> <encoding>UTF-8</encoding> </configuration> <executions> <execution> <phase>compile</phase> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <build> <resources> <resource> <targetPath>${project.build.directory}/classes</targetPath> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/*.xml</include> </includes> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.6</source> <target>1.6</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.test.testguava.app.App</mainClass> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>applicationContext.xml</resource> </transformer> </transformers> <shadedArtifactAttached>true</shadedArtifactAttached> <shadedClassifierName>executable</shadedClassifierName> </configuration> </execution> </executions> </plugin> </plugins> </build> 地址: http://blog.csdn.net/johnnywww/article/details/7964326 ...

Created 24246-08-24 · Updated 9096-10-09 · 1 min · 195 words · -

maven Scope

maven Scope maven依赖关系中Scope的作用 Dependency Scope 在 POM 4 中,<dependency> 中还引入了 <scope>,它主要管理依赖的部署。目前 <scope> 可以使用 5 个值: compile,缺省值,适用于所有阶段,会随着项目一起发布。 provided,类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet.jar。 runtime,只在运行时使用,如JDBC驱动,适用运行和测试阶段。 test,只在测试时使用,用于编译和运行测试代码。不会随项目发布。 system,类似provided,需要显式提供包含依赖的jar,Maven不会在Repository中查找它。 维护记录 时间 修改内容 原因 2026-10-09 修复本地 Hugo 构建的 Raw HTML 警告:代码/配置放入代码块,正文中的尖括号占位符改为行内代码;categories 改为 Java 正文中的 HTML/XML 片段被当作原始 HTML 丢弃;文件名/URL/标题按规范调整

Created 24246-08-24 · Updated 9096-10-09 · 1 min · 38 words · -

Java Unix Socket

Java Unix Socket http://www.oschina.net/p/juds/similar_projects?lang=0&sort=view Java Unix Domain Sockets (JUDS) 提供了 Java 的方法用来访问 Unix domain sockets socket 。 <dependency> <groupId>uk.co.caprica</groupId> <artifactId>juds</artifactId> <version>0.94.1</version> </dependency> 示例代码: package com.google.code.juds.test; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import com.google.code.juds.*; public class TestUnixDomainSocket { public static void main(String[] args) throws IOException { if (args.length != 1) { System.out .println("usage: java TestUnixDomainSocket socketfilename"); System.exit(1); } String socketFile = args[0]; byte[] b = new byte[128]; // Testcase 1.1: Test UnixDomainSocketClient with a stream socket UnixDomainSocketClient socket = new UnixDomainSocketClient(socketFile, UnixDomainSocket.SOCK_STREAM); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); in.read(b); System.out.println("Text received: \"" + new String(b) + "\""); String text = "[2] Hello! I'm the client!"; out.write(text.getBytes()); System.out.println("Text sent: " + "\"" + text + "\""); socket.close(); // Testcase 1.2: Test UnixDomainSocketClient with a datagram socket socket = new UnixDomainSocketClient(socketFile, UnixDomainSocket.SOCK_DGRAM); System.out.println("Provoke and catch an " + "UnsupportedOperationException:"); try { in = socket.getInputStream(); } catch (UnsupportedOperationException e) { System.out.println("UnsupportedOperationException has been " + "thrown as expected."); } out = socket.getOutputStream(); text = "[3] Hello! I'm the client!"; out.write(text.getBytes()); System.out.println("Text sent: \"" + text + "\""); socket.close(); // Testcase 2.1: Test UnixDomainSocketServer with a stream socket System.out.println("\nTest #2: Test UnixDomainSocketServer\nTestcase " + "2.1: Test UnixDomainSocketServer with a stream socket..."); UnixDomainSocketServer ssocket = new UnixDomainSocketServer(socketFile, UnixDomainSocket.SOCK_STREAM); in = ssocket.getInputStream(); out = ssocket.getOutputStream(); in.read(b); System.out.println("Text received: \"" + new String(b) + "\""); text = "[5] Hello! I'm the server!"; out.write(text.getBytes()); System.out.println("Text sent: " + "\"" + text + "\""); ssocket.close(); ssocket.unlink(); // Testcase 2.2: Test UnixDomainSocketServer with a datagram socket System.out.println("Testcase 2.2: Test UnixDomainSocketServer with " + "a datagram socket..."); ssocket = new UnixDomainSocketServer(socketFile, UnixDomainSocket.SOCK_DGRAM); System.out.println("Provoke and catch an " + "UnsupportedOperationException:"); in = ssocket.getInputStream(); try { out = ssocket.getOutputStream(); } catch (UnsupportedOperationException e) { System.out.println("UnsupportedOperationException has been " + "thrown as expected."); } in.read(b); System.out.println("Text received: \"" + new String(b) + "\""); ssocket.close(); ssocket.unlink(); } } 维护记录 时间 修改内容 原因 2026-10-09 修复本地 Hugo 构建的 Raw HTML 警告:代码/配置放入代码块,正文中的尖括号占位符改为行内代码;文件重命名为 unix-socket.md;categories 改为 Java 正文中的 HTML/XML 片段被当作原始 HTML 丢弃;文件名/URL/标题按规范调整

Created 26266-08-26 · Updated 9096-10-09 · 2 min · 323 words · -

LinkedTransferQueue

LinkedTransferQueue LinkedTransferQueue是在JDK1.7时,J.U.C包新增的一种比较特殊的阻塞队列,它除了具备阻塞队列的常用功能外,还有一个比较特殊的transfer方法。 我们知道,在普通阻塞队列中,当队列为空时,消费者线程(调用take或poll方法的线程)一般会阻塞等待生产者线程往队列中存入元素。而LinkedTransferQueue的transfer方法则比较特殊: 当有消费者线程阻塞等待时,调用transfer方法的生产者线程不会将元素存入队列,而是直接将元素传递给消费者; 如果调用transfer方法的生产者线程发现没有正在等待的消费者线程,则会将元素入队,然后会阻塞等待,直到有一个消费者线程来获取该元素。 有一篇论文讨论了其算法与性能: 地址: http://www.cs.rice.edu/~wns1/papers/2006-PPoPP-SQ.pdf LinkedTransferQueue 实现了一个重要的接口 TransferQueue, 该接口含有下面几个重要方法: 1. transfer(E e) 若当前存在一个正在等待获取的消费者线程,即立刻移交之;否则,会插入当前元素e到队列尾部,并且等待进入阻塞状态,到有消费者线程取走该元素。 2. tryTransfer(E e) 若当前存在一个正在等待获取的消费者线程 (使用take()或者poll()函数) ,使用该方法会即刻转移/传输对象元素e; 若不存在,则返回false,并且不进入队列。这是一个不阻塞的操作。 3. tryTransfer(E e, long timeout, TimeUnit unit) 若当前存在一个正在等待获取的消费者线程,会立即传输给它; 否则将插入元素e到队列尾部,并且等待被消费者线程获取消费掉, 若在指定的时间内元素e无法被消费者线程获取,则返回false,同时该元素被移除。 4. hasWaitingConsumer() 判断是否存在消费者线程 5. getWaitingConsumerCount() 获取所有等待获取元素的消费线程数量 其实transfer方法在SynchronousQueue的实现中就已存在了,只是没有做为API暴露出来。SynchronousQueue有一个特性:它本身不存在容量,只能进行线程之间的 元素传送。SynchronousQueue在执行offer操作时,如果没有其他线程执行poll,则直接返回false.线程之间元素传送正是通过transfer方法完成的。 有一个使用案例,我们知道ThreadPoolExecutor调节线程的原则是: 先调整到最小线程,最小线程用完后,他会将优先将任务放入缓存队列(offer(task)),等缓冲队列用完了,才会向最大线程数调节。这似乎与我们所理解的线程池模型有点不同。我们一般采用增加到最大线程后,才会放入缓冲队列中,以达到最大性能。ThreadPoolExecutor代码片段: public void execute(Runnable command) { if (command == null) throw new NullPointerException(); if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) { if (runState == RUNNING && workQueue.offer(command)) { if (runState != RUNNING || poolSize == 0) ensureQueuedTaskHandled(command); } else if (!addIfUnderMaximumPoolSize(command)) reject(command); // is shutdown or saturated } } 如果我们采用SynchronousQueue作为ThreadPoolExecuto的缓冲队列时,在没有线程执行poll时(即存在等待线程),则workQueue.offer(command)返回false,这时ThreadPoolExecutor就会增加线程,最快地达到最大线程数。但也仅此而已,也因为SynchronousQueue本身不存在容量,也决定了我们一般无法采用SynchronousQueue作为ThreadPoolExecutor的缓存队列。而一般采用LinkedBlockingQueue的offer方法来实现。最新的LinkedTransferQueue也许可以帮我们解决这个问题,后面再说。 ...

Created 14146-09-14 · Updated 9096-10-09 · 1 min · 187 words · -

LinkedBlockingQueue

LinkedBlockingQueue 基于链表的阻塞队列,同 ArrayBlockingQueue 类似,其内部也维持着一个数据缓冲队列 (该队列是一个链表) ,生产者存入的数据会缓存在队列内部,生产者立即返回;只有当队列缓冲区达到最大值缓存容量时 (LinkedBlockingQueue 可以通过构造函数指定该值) ,才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue 之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端 分别采用了独立的锁 来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。 作为开发者,我们需要注意的是,如果构造一个 LinkedBlockingQueue 对象,而没有指定其容量大小,LinkedBlockingQueue 会默认一个类似无限大小的容量 (Integer.MAX_VALUE) ,这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。 此队列按 FIFO (先进先出) 排序元素。队列的头部 是在队列中时间最长的元素。队列的尾部 是在队列中时间最短的元素。 新元素插入到队列的尾部,并且队列检索操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列, 但是在大多数并发应用程序中,其可预知的性能要低。 可选的容量范围构造方法参数作为防止队列过度扩展的一种方法。 如果未指定容量,默认容量为 Integer.MAX_VALUE ,容量范围可以在构造方法参数中指定作为防止队列过度扩展。 此对象是线程阻塞-线程安全的 不接受 null 元素 它实现了BlockingQueue接口。 实现了 Collection 和 Iterator 接口的所有可选方法。 在JDK5/6中,LinkedBlockingQueue和ArrayBlocingQueue等对象的poll(long timeout, TimeUnit unit)存在内存泄露Leak的对象AbstractQueuedSynchronizer.Node,据称JDK5会在Update12里Fix,JDK6会在Update2里Fix 下面介绍几种常用的方法: 定义一个输出方法: static void v(Object s){System.out.println(s.toString());} LinkedBlockingQueue<String> bq=new LinkedBlockingQueue<String>(); for(int i=0;i<100;i++) { bq.add("i"+i);//如果空间已满,此方法会抛出异常,所以这就是put,或者offer方法的优势所在 } String s1=bq.take();//i0 String s2=bq.take();//i1 bq.offer("ix",5,TimeUnit.SECONDS);//在尾部插入一个元素,如果有必要 ,等待 指定的时间,使得队列变得可用。返回boolean值 表示是否插入成功。 bq.put("ixx");//将指定的元素添加到队列的尾部,如有必要,则等待空间变得可用,如果空间满了,则会一直等到空间可用时,进行插入。 bq.poll();//poll() //poll(long timeout, TimeUnit unit) 检索并移除此队列的头,如果此队列为空,则返回 null。 bq.clear() 从队列彻底移除所有元素。 ...

Created 14146-09-14 · Updated 9096-10-09 · 3 min · 452 words · -

Linux JMeter 分布式测试

linux jmeter 分布式测试 http://www.51testing.com/html/55/383255-847895.html 我将使用3台linux测试机部署jmeter,部署方法也是非常简单,打包后放在指定的目录就ok了, 先定义了 A服务器:控制机 B C服务器为负载机 主意如果要运行jmeter一定要安装1.6以上版本的jdk并正确配置环境变量, 首先在bin目录下 启动B C 服务器 jmeter的jmeter-server服务器,如果启动报错请根据报错内容检查对应的环境配置,我这边报错的原因是hosts没有指定地址, jmeter-server正常启动会提示"创建远程服务" 接下来是准备测试脚本,可以在windows环境下先创建jmx文件,主意最好不要添加监听器,应为命令行启动的话监听器可能会占用资源而且有没有任何视图效果. 将生成好的jmx文件上传到A服务器 jmeter目录的bin目录下,然后在bin目录下创建xx.jtl文件.jtl文件用来接收测试中产生的测试结果 好了 现在进bin目录 打入启动命令,当然你也可以用绝对路径来运行启动命令 ./jmeter -n -t xx.jmx -R B服务器ip,C服务器ip -l $jmeterpath/bin/xx.jtl 参数说明 : -n 告诉jmeter使用nogui模式运行测试 -t 执行的测试脚本名 -R 后面跟随负载机的ip地址 ,注意用逗号隔开 -l 后面跟着测试结果记录的路径与文件名,主意这个文件jmeter不会自己创建,请预先创建好, 万事俱备 按下回车 ! 就开始虐待你的测试项目吧 测试完成后把xx.jtl文件下载到windows机上在不同的监听器上分析测试结果 如果想修改测试脚本,起始也不必把脚本在windows机上打开gui界面修改,直接编辑.jmx文件 你就可以看到许多熟悉的名字,这里就简单介绍几个 </elementProp> <stringProp name="ThreadGroup.num_threads">5</stringProp> <stringProp name="ThreadGroup.ramp_time">5</stringProp> <longProp name="ThreadGroup.start_time">1281132211000</longProp> <longProp name="ThreadGroup.end_time">1281132211000</longProp> <boolProp name="ThreadGroup.scheduler">true</boolProp> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <stringProp name="ThreadGroup.duration">60</stringProp> <stringProp name="ThreadGroup.delay">5</stringProp> </ThreadGroup> 看吧xml格式的测试脚本,很清楚则么改了吧 ThreadGroup.num_threads 线程数 ThreadGroup.ramp_time 全部线程启动完成的时间 ThreadGroup.duration 测试的持续时间 还是很容易看明白的.哈哈 这次测试运维把tomcat的内存占用开到了36g 并发线程开到了500万 但是最终在jmeter800线程的压力下还是变成了僵尸进程. 维护记录 时间 修改内容 原因 2026-10-09 修复本地 Hugo 构建的 Raw HTML 警告:代码/配置放入代码块,正文中的尖括号占位符改为行内代码;文件重命名为 jmeter-distributed-testing.md;title 改为「Linux JMeter 分布式测试」;url 改为 jmeter-distributed-testing;旧 url 加入 aliases;categories 改为 Tools 正文中的 HTML/XML 片段被当作原始 HTML 丢弃;文件名/URL/标题按规范调整

Created 16166-09-16 · Updated 9096-10-09 · 1 min · 104 words · -

kill, killall, signal

kill, killall, signal kill by key ps -ef |grep java ps -ef |grep java|awk '{print $2}'|xargs -t -n 1 kill -9 ps -ef |grep java killall 命令 killall命令杀死同一进程组内的所有进程。其允许指定要终止的进程的名称,而非PID。 killall java http://www.cnblogs.com/itech/archive/2012/03/05/2380794.html kill命令用于终止指定的进程 (terminate a process) ,是Unix/Linux下进程管理的常用命令。通常,我们在需要终止某个或某些进程时,先使用ps/pidof/pstree/top等工具获取进程PID,然后使用kill命令来杀掉该进程。kill命令的另外一个用途就是向指定的进程或进程组发送信号 (The command kill sends the specified signal to the specified process or process group) ,或者确定进程号为PID的进程是否还在。比如,有许多程序都把 SIGHUP 信号作为重新读取配置文件的触发条件。 一 常用参数 格式: kill <pid> 格式: kill -TERM <pid> 发送SIGTERM信号到指定进程,如果进程没有捕获该信号,则进程终止 (If no signal is specified, the TERM signal is sent. The TERM signal will kill processes which do not catch this signal.) ...

Created 17176-09-17 · Updated 9096-10-09 · 5 min · 992 words · -

jmap

jmap 、基本参数: -dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件. -finalizerinfo 打印正等候回收的对象的信息. -heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况. -histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀"*". 如果live子参数加上后,只统计活的对象数量. -permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来. -F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效. -h | -help 打印辅助信息 -J 传递参数给jmap启动的jvm. pid 需要被打印配相信息的java进程id,创业与打工的区别 - 博文预览,可以用jps查问. 4、使用示例 1)、[fenglb@ccbu-156-5 ~]$ jmap -histo 4939 sudo ./jmap -dump:format=b,file=/tmp/917dump.dat 8949 If you take a look at the source code for com.sun.tools.hat.internal.parser.Reader, you'll see that it's looking for the magic number 0x4a415641. This value is used to help identify valid heap dump files. jmap should append this value as the first four bytes of any heap dump file it creates. http://stackoverflow.com/questions/15507047/jhat-throwing-unrecognized-magic-number http://blog.csdn.net/gtuu0123/article/details/6039474 维护记录 时间 修改内容 原因 2026-10-09 修复本地 Hugo 构建的 Raw HTML 警告:代码/配置放入代码块,正文中的尖括号占位符改为行内代码;categories 改为 Java 正文中的 HTML/XML 片段被当作原始 HTML 丢弃;文件名/URL/标题按规范调整

Created 17176-09-17 · Updated 9096-10-09 · 1 min · 111 words · -

Netty 下载文件

Netty 下载文件 http://www.open-open.com/lib/view/open1409642102932.html 本实例主要参考的是官网的examples: 点击这里使用场景: 客户端向Netty请求一个文件,Netty服务端下载指定位置文件到客户端。 本实例使用的是Http协议,当然,可以通过简单的修改即可换成TCP协议。 需要注意本实例的关键点是,为了更高效的传输大数据,实例中用到了ChunkedWriteHandler编码器,它提供了以zero-memory-copy方式写文件。 第一步: 先写一个HttpFileServer ? package NettyDemo.file.server; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.stream.ChunkedWriteHandler; /***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\**** * Reserved. BidPlanStructForm.java Created on 2014-8-19 Author: <a * href=mailto:wanghouda@126.com>wanghouda * @Title: HttpFileServer.java * @Package NettyDemo.file.server Description: Version: 1.0 \***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***\***/ public class HttpFileServer { static final int PORT = 8080; public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<SocketChannel>() {// 有连接到达时会创建一个channel @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new FileServerHandler()); } }); Channel ch = b.bind(PORT).sync().channel(); System.err.println("打开浏览器,输入: " + ("http") + "://127.0.0.1:" + PORT + '/'); ch.closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } 第二步: 再写一个FileServerHandler? ...

Created 17176-09-17 · Updated 9096-10-09 · 6 min · 1151 words · -

Regex Lazy Matching 正则惰性匹配 .*?

正则, 惰性匹配(lazy) .*? http://www.nowamagic.net/librarys/veda/detail/1038 使用场景 有时,我们想用正则匹配以某个子串开头,且以某个子串或字符结尾的子字符串,但是结尾的字串或字符在原字符串中出现了多次,但我们只想匹配从开始处到第一次出现的地方,换句话说,想得到开始和结尾之间内容最少的匹配。 正则的贪婪与非贪婪(惰性) 通常使用如下字符类描述前导字符的重复特征: ?: 告诉引擎匹配前导字符0次或一次。事实上是表示前导字符是可选的。 +: 告诉引擎匹配前导字符1次或多次。 *: 告诉引擎匹配前导字符0次或多次。 {min, max}: 告诉引擎匹配前导字符min次到max次。min和max都是非负整数。如果有逗号而max被省略了,则表示max没有限制;如果逗号和max都被省略了,则表示重复min次。 因此 {0,} 和 * 一样,{1,} 和 + 的作用一样。 贪婪 默认情况下,? + * {min, max}都是贪婪的,也就是说,它会根据前导字符去匹配尽可能多的内容。 非贪婪(惰性) 非贪婪就是匹配尽可能少的内容。 原理浅析 结合实例来分析哈基于正则的引擎对文本的匹配过程。原始字符串:This is a <EM>first</EM> test,使用正则 <.+> 来匹配HTML标签,期望第一次匹配得到<EM>,第二次匹配得到</EM>,实际却是第一次匹配就得到了 <EM>first</EM>。 来看看匹配过程,第一个记号是 <,这是一个文本字符,匹配其自身。第二个符号是.,匹配了字符E,然后+一直可以匹配其余的字符,直到一行的结束。然后到了换行符,匹配失败(.不匹配换行符)。于是引擎开始对下一个正则表达式符号进行匹配,即试图匹配 >。到目前为止,<.+ 已经匹配了 <EM>first</EM> test。引擎会试图将 > 与换行符进行匹配,结果失败了。于是引擎进行回溯。回溯后的匹配状况是 <.+ 匹配 <EM>first</EM> tes。于是引擎将 > 与 t 进行匹配。显然还是会失败。这个过程继续,直到 <.+ 匹配 <EM>first</EM,> 与 > 匹配。于是引擎找到了一个匹配 <EM>first</EM>。记住,正则导向的引擎是急切的,所以它会急着报告它找到的第一个匹配。而不是继续回溯,即使可能会有更好的匹配,例如<EM>。所以我们可以看到,由于+的贪婪性,使得正则表达式引擎返回了一个最左边的最长的匹配。 如果想得到期望的结果,就需要启用非贪婪模式:<.+?> 总结:如果是贪婪匹配模式,正则引擎会一直匹配到字符串最后;当匹配为false时,就回溯以找到倒数第一个匹配位置,返回匹配结果。 如果是非贪婪匹配模式,正则引擎会匹配到符合pattern的末尾位置那个字符,然后再往后走一步,发现匹配为false时,就回溯以找到最近一个匹配为true的位置,返回匹配结果。 实例 例如,原始字符串: ...

Created 24246-09-24 · Updated 9096-10-09 · 1 min · 103 words · -

Blocking, Non-blocking, Sync, Async 阻塞、非阻塞、同步、异步

阻塞,非阻塞,同步,异步 作者: 萧萧 链接: https://www.zhihu.com/question/19732473/answer/241673170 来源: 知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 IO 概念区分四个相关概念: 同步 (Synchronous) 异步( Asynchronous) 阻塞( Blocking ) 非阻塞( Nonblocking) 这四个概念的含义以及相互之间的区别与联系,并不如很多网络博客所写的那么简单, 通过举一些什么商店购物, 买书买报的例子就能讲清楚。 进程间通信的同步/异步, 阻塞/非阻塞首先强调一点, 网络上很多博文关于同步/异步, 阻塞非阻塞区别的解释其实都经不起推敲。 例如在 严肃 的这一高赞回答中 , 有如下解释 (不准确) : 同步/异步关注的是消息通信机制 (synchronous communication/ asynchronous communication) 。 所谓同步,就是在发出一个调用时,在没有得到结果之前, 该调用就不返回。异步则是相反,调用在发出之后,这个调用就直接返回了, 所以没有返回结果阻塞/非阻塞关注的是程序在等待调用结果 (消息,返回值) 时的状态.阻塞调用是指调用结果返回之前,当前线程会被挂起。 调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。粗一看, 好像同步/ 非同步, 阻塞/非阻塞 是两种维度的概念, 可以分别对待, 但是稍微推敲一下就会发现上述的解释存在不妥之处。如果“同步”是发起了一个调用后, 没有得到结果之前不返回, 那它毫无疑问就是被“阻塞”了 (即调用进程处于 “waiting” 状态) 。如果“异步”调用发出了以后就直接返回了, 毫无疑问, 这个进程没有被“阻塞”。 所以, 上述的解释是不准确的。 让我们看一下《操作系统概念 (第九版) 》中有关进程间通信的部分是如何解释的: 翻译一下就是: 进程间的通信是通过 send() 和 receive() 两种基本操作完成的。具体如何实现这两种基础操作,存在着不同的设计。 消息的传递有可能是阻塞的或非阻塞的 – 也被称为同步或异步的: 阻塞式发送 (blocking send) . 发送方进程会被一直阻塞, 直到消息被接受方进程收到。非阻塞式发送 (nonblocking send) 。 发送方进程调用 send() 后, 立即就可以其他操作。阻塞式接收 (blocking receive) 接收方调用 receive() 后一直阻塞, 直到消息到达可用。非阻塞式接受 (nonblocking receive) 接收方调用 receive() 函数后, 要么得到一个有效的结果, 要么得到一个空值, 即不会被阻塞。上述不同类型的发送方式和不同类型的接收方式,可以自由组合。也就是说, 从进程级通信的维度讨论时, 阻塞和同步 (非阻塞和异步) 就是一对同义词, 且需要针对发送方和接收方作区分对待。———- 下面对理解同步异步,阻塞非阻塞所需的知识点进行详细叙述———————先修知识用户空间和内核空间进程切换 系统调用 (system call) 中断 (interrupt) 进程的阻塞用户空间和内核空间操作系统为了支持多个应用同时运行,需要保证不同进程之间相对独立 (一个进程的崩溃不会影响其他的进程 , 恶意进程不能直接读取和修改其他进程运行时的代码和数据) 。 因此操作系统内核需要拥有高于普通进程的权限, 以此来调度和管理用户的应用程序。于是内存空间被划分为两部分,一部分为内核空间,一部分为用户空间,内核空间存储的代码和数据具有更高级别的权限。内存访问的相关硬件在程序执行期间会进行访问控制 ( Access Control) ,使得用户空间的程序不能直接读写内核空间的内存。有《微机原理》 课程基础同学可以 Google 搜索 DPL, CPL 这两个关键字了解硬件层面的内存访问权限控制细节进程切换上图展示了进程切换中几个最重要的步骤: 当一个程序正在执行的过程中, 中断 (interrupt) 或 系统调用 (system call) 发生可以使得 CPU 的控制权会从当前进程转移到操作系统内核。操作系统内核负责保存进程 i 在 CPU 中的上下文 (程序计数器, 寄存器) 到 PCBi (操作系统分配给进程的一个内存块) 中。从 PCBj 取出进程 j 的CPU 上下文, 将 CPU 控制权转移给进程 j , 开始执行进程 j 的指令。几个底层概念的通俗 (不严谨) 解释: 中断 (interrupt) CPU 微处理器有一个中断信号位, 在每个CPU时钟周期的末尾, CPU会去检测那个中断信号位是否有中断信号到达, 如果有, 则会根据中断优先级决定是否要暂停当前执行的指令, 转而去执行处理中断的指令。 (其实就是 CPU 层级的 while 轮询) 时钟中断( Clock Interrupt )一个硬件时钟会每隔一段 (很短) 的时间就产生一个中断信号发送给 CPU,CPU 在响应这个中断时, 就会去执行操作系统内核的指令, 继而将 CPU 的控制权转移给了操作系统内核, 可以由操作系统内核决定下一个要被执行的指令。系统调用 (system call) system call 是操作系统提供给应用程序的接口。 用户通过调用 systemcall 来完成那些需要操作系统内核进行的操作, 例如硬盘, 网络接口设备的读写等。从上述描述中, 可以看出来, 操作系统在进行进切换时,需要进行一系列的内存读写操作, 这带来了一定的开销: 对于一个运行着 UNIX 系统的现代 PC 来说, 进程切换通常至少需要花费 300 us 的时间进程阻塞上图展示了一个进程的不同状态: New. 进程正在被创建.Running. 进程的指令正在被执行Waiting. 进程正在等待一些事件的发生 (例如 I/O 的完成或者收到某个信号) Ready. 进程在等待被操作系统调度Terminated. 进程执行完毕 (可能是被强行终止的) 我们所说的 “阻塞”是指进程在发起了一个系统调用 (System Call) 后, 由于该系统调用的操作不能立即完成,需要等待一段时间,于是内核将进程挂起为等待 (waiting) 状态, 以确保它不会被调度执行, 占用 CPU 资源。友情提示: 在任意时刻, 一个 CPU 核心上 (processor) 只可能运行一个进程 。I/O System Call 的阻塞/非阻塞, 同步/异步这里再重新审视 阻塞/非阻塞 IO 这个概念, 其实阻塞和非阻塞描述的是进程的一个操作是否会使得进程转变为“等待”的状态, 但是为什么我们总是把它和 IO 连在一起讨论呢?原因是, 阻塞这个词是与系统调用 System Call 紧紧联系在一起的, 因为要让一个进程进入 等待 (waiting) 的状态, 要么是它主动调用 wait() 或 sleep() 等挂起自己的操作, 另一种就是它调用 System Call, 而 System Call 因为涉及到了 I/O 操作, 不能立即完成, 于是内核就会先将该进程置为等待状态, 调度其他进程的运行, 等到 它所请求的 I/O 操作完成了以后, 再将其状态更改回 ready 。操作系统内核在执行 System Call 时, CPU 需要与 IO 设备完成一系列物理通信上的交互, 其实再一次会涉及到阻塞和非阻塞的问题, 例如, 操作系统发起了一个读硬盘的请求后, 其实是向硬盘设备通过总线发出了一个请求,它即可以阻塞式地等待IO 设备的返回结果,也可以非阻塞式的继续其他的操作。 在现代计算机中,这些物理通信操作基本都是异步完成的, 即发出请求后, 等待 I/O 设备的中断信号后, 再来读取相应的设备缓冲区。 但是,大部分操作系统默认为用户级应用程序提供的都是阻塞式的系统调用 (blocking systemcall) 接口, 因为阻塞式的调用,使得应用级代码的编写更容易 (代码的执行顺序和编写顺序是一致的) 。但同样, 现在的大部分操作系统也会提供非阻塞I/O 系统调用接口 (Nonblocking I/O system call) 。 一个非阻塞调用不会挂起调用程序, 而是会立即返回一个值, 表示有多少bytes 的数据被成功读取 (或写入) 。非阻塞I/O 系统调用( nonblocking system call )的另一个替代品是 异步I/O系统调用 (asychronous system call) 。 与非阻塞 I/O 系统调用类似,asychronous system call 也是会立即返回, 不会等待 I/O 操作的完成, 应用程序可以继续执行其他的操作, 等到 I/O 操作完成了以后,操作系统会通知调用进程 (设置一个用户空间特殊的变量值 或者 触发一个 signal 或者 产生一个软中断 或者 调用应用程序的回调函数) 。此处, 非阻塞I/O 系统调用( nonblocking system call ) 和 异步I/O系统调用 (asychronous system call) 的区别是: 一个非阻塞I/O 系统调用 read() 操作立即返回的是任何可以立即拿到的数据, 可以是完整的结果, 也可以是不完整的结果, 还可以是一个空值。而异步I/O系统调用 read () 结果必须是完整的, 但是这个操作完成的通知可以延迟到将来的一个时间点。下图展示了同步I/O 与 异步 I/O 的区别 (非阻塞 IO 在下图中没有绘出) . 注意, 上面提到的 非阻塞I/O 系统调用( nonblocking system call ) 和 异步I/O系统调用 都是非阻塞式的行为 (non-blocking behavior) 。 他们的差异仅仅是返回结果的方式和内容不同。非阻塞 I/O 如何帮助服务器提高吞吐量考虑一个单进程服务器程序, 收到一个 Socket 连接请求后, 读取请求中的文件名,然后读请求的文件名内容,将文件内容返回给客户端。 那么一个请求的处理流程会如下图所示。R 表示读操作W 表示写操作C 表示关闭操作在这个过程中, 我们可以看到, CPU 和 硬盘IO 的资源大部分时间都是闲置的。 此时, 我们会希望在等待 I/O 的过程中继续处理新的请求。方案一: 多进程每到达一个请求, 我们为这个请求新创建一个进程来处理。 这样, 一个进程在等待 IO 时, 其他的进程可以被调度执行, 更加充分地利用 CPU 等资源。问题: 每新创建一个进程都会消耗一定的内存空间, 且进程切换也会有时间消耗, 高并发时, 大量进程来回切换的时间开销会变得明显起来。方案二: 多线程和多进程方案类似,为每一个请求新建一个线程进行处理,这样做的重要区别是, 所有的线程都共享同一个进程空间问题: 需要考虑是否需要为特定的逻辑使用锁。引申问题: 一个进程中的某一个线程发起了 system call 后, 是否造成整个进程的阻塞? 如果会, 那么多线程方案与单进程方案相比就没有明显的改善。解决办法1: 内核支持的线程 (kenerl supported threads) 操作系统内核能够感知到线程, 每一个线程都会有一个内核调用栈 (kenerl stack) 和 保存CPU 寄存器下文的 table 。在这种方案中, 如果 CPU 是多核的, 不同的线程还可以运行在不同的 CPU processor 上。 既实现了IO 并发, 也实现了 CPU 并发。问题: 内核支持线程可移植性差, 其实现对于不同的操作系统而言有所差别。解决办法2: 用户支持的线程 (user supported threads) 内核感知不到用户线程, 每一个用户的进程拥有一个调度器, 该调度器可以感知到线程发起的系统调用, 当一个线程产生系统调用时, 不阻塞整个进程, 切换到其他线程继续运行。 当 I/O 调用完成以后, 能够重新唤醒被阻塞的线程。实现细节: 应用程序基于线程库 thread libray 编写线程库中包含 “虚假的” read(), write(), accept()等系统调用。线程库中的 read(), write(), accept() 的底层实现为非阻塞系统调用 (Non-blocking system call) , 调用后,由于可以立即返回, 则将特定的线程状态标记为 waiting, 调度其他的可执行线程。 内核完成了 IO 操作后, 调用线程库的回调函数, 将原来处于 waiting 状态的线程标记为 runnable.从上面的过程可以看出,用户级支持线程 (User-Supported Threads) 的解决方案基于非阻塞IO系统调用( non-blocking system call) , 且是一种基于操作系统内核事件通知 (event-driven) 的解决方案, 该方案可以降低系统处理并发请求时的进程切换开销。 基于这个方案, 可以引申到更为宽泛的 event-driven progreamming 话题上。 但是这里就不作赘述了。总结: 阻塞/非阻塞, 同步/异步的概念要注意讨论的上下文: 在进程通信层面, 阻塞/非阻塞, 同步/异步基本是同义词, 但是需要注意区分讨论的对象是发送方还是接收方。发送方阻塞/非阻塞 (同步/异步) 和接收方的阻塞/非阻塞 (同步/异步) 是互不影响的。在 IO 系统调用层面 ( IO system call ) 层面, 非阻塞 IO 系统调用 和 异步 IO 系统调用存在着一定的差别, 它们都不会阻塞进程, 但是返回结果的方式和内容有所差别, 但是都属于非阻塞系统调用 ( non-blocing system call ) 2. 非阻塞系统调用 (non-blocking I/O system call 与 asynchronous I/O system call) 的存在可以用来实现线程级别的 I/O 并发, 与通过多进程实现的 I/O 并发相比可以减少内存消耗以及进程切换的开销。编辑于 03-02 ...

Created 13136-11-13 · Updated 9096-10-09 · 3 min · 607 words · -

float double NaN

float double NaN http://zranye.iteye.com/blog/1344880 http://blog.csdn.net/naruto_ahu/article/details/8805808 (注意,double中的NaN类似) 在帮助文档中,NaN是这样解释的 (double的,和float差不多) : public static final double NaN 保存 double 类型的 NaN 值的常量。它等于 Double.longBitsToDouble(0x7ff8000000000000L) 返回的值。 我是这样解释的 (理解不深,有错请不吝赐教) : NaN-Not-a-Number,其实是指一个不合理的实数,在合法却不合理的情况下避免你的程序出现Exception。 NaN什么也不是,如果理解成一个超越了float范围的常量 (或者double) ,那也是不对的,为什么?试试判断 (NaN==NaN) 吧,你会发现这返回了一个false。这在java底层是怎么做到的呢?最直接的方式是看java源代码咯。 /** A constant holding a Not-a-Number (NaN) value of type float. It is equivalent to the value returned by Float.intBitsToFloat(0x7fc00000). */ public static final float NaN = 0.0f / 0.0f; 上面这一段,就是floa的NaN定义了。这并不是一个超出了范围的常量,而是java的Float中人为得制造出来的一个合法但是不合理的实数了 (看来那位仁兄说得基本都对了) 。所以,NaN==NaN永远等于false啦。 那用什么来判断是否合理的Number呢?很奇怪的是,java提供了一个isNaN()的方法,指是否是一个不合理的实数。是否很拗口?我在javascript中第一次接触这个东西,也觉得很奇怪。 为什么有这样一个判断的方法不把NaN设置成private的,却又对外开放?难道担心我们不能主动制造一个不合理的实数出来?本人菜鸟,如果有高手能解答,还请不吝赐教,理解未深,有错误请指出,谢谢。 维护记录 时间 修改内容 原因 2026-10-09 修复本地 Hugo 构建的 Raw HTML 警告:代码/配置放入代码块,正文中的尖括号占位符改为行内代码;categories 改为 Java 正文中的 HTML/XML 片段被当作原始 HTML 丢弃;文件名/URL/标题按规范调整

Created 4046-12-04 · Updated 9096-10-09 · 1 min · 83 words · -

netty http client

netty http client http://www.cnblogs.com/luxiaoxun/p/3959450.html 基于Netty4的HttpServer和HttpClient的简单实现 Netty的主页: http://netty.io/index.html 使用的Netty的版本: netty-4.0.23.Final.tar.bz2 ‐ 15-Aug-2014 (Stable, Recommended) Http 消息格式: Http request: Method path-to-resource HTTPVersion-number Header-name-1: value1 Header-name-2: value2 Optional request body Http response: HTTP/Version-number response-code response-phrase Header-name-1: value1 Header-name-2: value2 Optional response body 实现一个简单的Http请求及响应过程: Client向Server发送http请求。 Server端对http请求进行解析。 Server端向client发送http响应。 Client对http响应进行解析。 Netty中Http request消息格式: Netty中Http response消息格式: 代码实例: Http Server: package com.netty.test; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; public class HttpServer { private static Log log = LogFactory.getLog(HttpServer.class); public void start(int port) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { // server端发送的是httpResponse,所以要使用HttpResponseEncoder进行编码 ch.pipeline().addLast(new HttpResponseEncoder()); // server端接收到的是httpRequest,所以要使用HttpRequestDecoder进行解码 ch.pipeline().addLast(new HttpRequestDecoder()); ch.pipeline().addLast(new HttpServerInboundHandler()); } }).option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { HttpServer server = new HttpServer(); log.info("Http Server listening on 8844 ..."); server.start(8844); } } 响应请求的HttpServerInboundHandler: ...

Created 17176-12-17 · Updated 9096-10-09 · 2 min · 400 words · -

appassembler-maven-plugin

appassembler-maven-plugin Goals Overview [appassembler:assemble][1] Assembles the artifacts and generates bin scripts for the configured applications. [appassembler:create-repository][2] Creates an appassembler repository. [appassembler:generate-daemons][3] Generates JSW based daemon wrappers. maven 自动生成运行脚本插件appassembler-maven-plugin 博客分类: maven appassembler-maven-plugin可以自动生成跨平台的启动脚本,省去了手工写脚本的麻烦,而且还可以生成jsw的后台运行程序。 appassembler的配置比较简单,在pom.xml的配置文件加入插件配置。 比如说不同的启动脚本,可以如下配置 <plugin> <groupId>org.codehaus.mojo</groupId> appassembler-maven-plugin</artifactId> <version>1.2.1</version> <configuration> <configurationDirectory>conf</configurationDirectory> <configurationSourceDirectory>src/main/resources</configurationSourceDirectory> <copyConfigurationDirectory>true</copyConfigurationDirectory> <includeConfigurationDirectoryInClasspath>true</includeConfigurationDirectoryInClasspath> ${project.build.directory}/chj-search-client</assembleDirectory> <extraJvmArguments>-Xms128m</extraJvmArguments> <binFileExtensions> <unix>.sh</unix> </binFileExtensions> <platforms> <platform>windows</platform> <platform>unix</platform> </platforms> <repositoryName>lib</repositoryName> <programs> <program> <mainClass>com.chj360.search.client.App</mainClass> </program> </programs> </configuration> </plugin> 然后运行maven命令 :mvn package appassembler:assemble 就可以自动生成整个的依赖文件,配置文件和运行脚本了。 一些配置说明 configurationDirectory: 生成配置文件路径 configurationSourceDirectory: 配置文件原路径,默认为src/main/config assembleDirectory:整体包目录 ...

Created 5056-01-05 · Updated 9096-10-09 · 1 min · 184 words · -