Exception Handling

异常捕捉的确是对性能有影响的,那是因为一旦异常被抛出,函数也就跟着 return 了。而程序在执行时需要处理 函数栈 的上下文,这会导致性能变得很慢,尤其是线程栈比较深的时候。但从另一方面来说,异常的抛出基本上表明程序的错误。程序在绝大多数情况下,应该是在没有异常的情况下运行的, 所以,有异常的情况应该是少数的情况,不会影响正常处理的性能问题。 对于我们并不期望会发生的事,我们可以使用异常捕捉;对于我们觉得可能会发生的事,使用返回码。 https://time.geekbang.org/column/article/675 你觉得自己是一个Java专家吗?是否肯定自己 已经全面掌握了Java的异常处理机制?在下面这段代码中,你能够迅速找出异常处理的六个问题吗? 1 OutputStreamWriter out = … 2 java.sql.Connection conn = … 3 try { // ⑸ 4 Statement stat = conn.createStatement(); 5 ResultSet rs = stat.executeQuery( 6 “select uid, name from user”); 7 while (rs.next()) 8 { 9 out.println(“ID: " + rs.getString(“uid”) // ⑹ 10 “,姓名: " + rs.getString(“name”)); 11 } 12 conn.close(); // ⑶ 13 out.close(); 14 } 15 catch(Exception ex) // ⑵ 16 { 17 ex.printStackTrace(); //⑴,⑷ ...

2026-03-23 · 6 min · 1146 words · -

JMM, Java Memory Model, java 内存模型

JMM, Java Memory Model, java 内存模型 Java内存模型,是指Java程序在运行时内存的模型,而Java代码是运行在Java虚拟机之上的,由Java虚拟机通过解释执行(解释器)或编译执行(即时编译器)来完成, 所以Java内存模型,也就是指Java虚拟机的运行时内存模型。 Java内存比较流行的说法便是堆和栈,这其实是非常粗略的一种划分,这种划分的"堆"对应内存模型的Java堆,“栈"是指虚拟机栈,然而Java内存模型远比这更复杂 @startuml skinparam componentStyle rectangle skinparam nodesep 10 skinparam ranksep 10 component "Java Run-time Data Areas" { [heap] component thread0 { [PC Register] as pc0 [thread stack] as ts0 [Native Method Stack] as nms0 pc0 -[hidden]-> ts0 ts0 -[hidden]-> nms0 } component thread1 { [PC Register] as pc1 [thread stack] as ts1 [Native Method Stack] as nms1 pc1 -[hidden]-> ts1 ts1 -[hidden]-> nms1 } thread0 --[hidden]--> heap thread1 --[hidden]--> heap component "method area" as ma { [constant pool] as cp } heap -[hidden]-> ma } @enduml Java 虚拟机的内存空间 (运行时数据区)分为 5 个部分: 程序计数器, PC register Java 虚拟机栈, JVM stack, 线程栈, thread stack 本地方法栈, native method stack 堆, heap 方法区, method area,方法区里面还有一个常量池 线程私有的数据区: 程序计数器,虚拟机栈,本地方法栈 所有线程共有的数据区: Java堆,方法区 ...

2021-09-21 · 3 min · 596 words · -

自动装箱 拆箱

自动装箱 拆箱 Java作为面向对象语言,有人认为所看到的都是对象,事实上,在Java SE 5之前,基本类型默认并不是采用对象存在的如果您想要把基本类型作为对象来处理,就必须自行转换,不过,在Java SE 5之后,为基本数据类型提供了自动装箱和拆箱功能,使得将基本类型转换为对象变得极其便捷。 在这里来捋一捋java的基本数据类型,不理不要紧,一理才发现俺也掌握的不是那么明确,在这里俺也再次学习下 总计有八个,分别是 byte字节型 (一个字节) ,char字符型 (两个字节) ,short短整型 (两个字节) ,int整型 (四个字节) , long长整型 (八个字节) ,float浮点型 (四个字节) ,double 双精度浮点型 (八个字节) ,boolean型 (一个字节) 在javase5之前,如果想要把基本数据类型作为对象来操作,就需要采用对应的对象,来把它们打包才行 现在虽然不用这样了,但其中也有一些要注意的地方,俺揪出来晒晒。 先说说类和对象,建立个概念吧先 类-可以认为是对象的设计图 对象-是按照设计图实现了的具体工具 先这么简单理解吧,如果真要扯开了说,那可是软件工程里面的一门专业课,我们有个概念就好 之所以要将基本类型数据打包成为对象,原因很简单,是因为对象可以携带更多的数据。 手动、自动装箱拆箱示例 Long,Integer,Double,Float,Boolean等等的类就是所谓的wrapper类,就跟wrapper这个单词所代表的意思一样,就是提供一个"包装,加壳",把基本数据类型放在里面,来看代码,体会下先 public class WrapperDemo{ public staticvoid main(String[] args){ int data1=21; int data2=24; //打包成为对象 Integer data1Wrapper = new Integer(data1); Integer data2Wrapper = new Integer(data2); //原始数据直接除以3 System.out.println(data1/3); //将数据打包,转换为double型,除以3 System.out.println(data1Wrapper.doubleValue()/3); //比较 System.out.println(data1Wrapper.compareTo(data2Wrapper)); } } 图1-1 WrapperDemo的运行结果 通过上面的代码和运行结果,看到了将基本数据类型打包成为对象带来的好处了吧,别着急,这还只是javase5之前的做法,在javase5之后就已经支持自动装箱和拆箱了,在这,就不再单独写代码出来了,只写几个能说明问题的语句就可以了,相信很容易理解的。 javase5之前,手动打包 Integer data1 = new Integer(10); ...

2020-06-30 · 1 min · 201 words · -

java maven 可执行 jar/ executable jar, maven-assembly-plugin

java maven 可执行 jar/ executable jar, maven-assembly-plugin 在pom中加入 maven-assembly-plugin <project> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>com.wiloon.java.lock.TestFutex</mainClass> </manifest> </archive> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project> mvn clean package assembly:single # 如果没执行过 mvn package 会报错: Cannot include project artifact: com.wiloon.java:pingd-java:jar:1.0-SNAPSHOT; it doesn't have an associated file or directory. java -jar target/pingd-java-1.0-SNAPSHOT-jar-with-dependencies.jar

2016-12-16 · 1 min · 63 words · -

java UDP

java UDP 一. UDP协议定义 UDP协议的全称是用户数据报,在网络中它与TCP协议一样用于处理数据包。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据报分组、组装和不能对数据包的排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。 二. 使用UDP的原因 它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。比如我们聊天用的ICQ和OICQ就是使用的UDP协议。在选择使用协议的时候,选择UDP必须要谨慎。在网络质量令人不十分满意的环境下,UDP协议数据包丢失会比较严重。 三. 在Java中使用UDP协议编程的相关类 InetAddress 用于描述和包装一个Internet IP地址。有如下方法返回实例: getLocalhost(): 返回封装本地地址的实例。 getAllByName(String host): 返回封装Host地址的InetAddress实例数组。 getByName(String host): 返回一个封装Host地址的实例。其中,Host可以是域名或者是一个合法的IP地址。 InetAddress.getByAddress(addr): 根据地址串返回InetAddress实例。 InetAddress.getByAddress(host, addr): 根据主机地符串和地址串返回InetAddress实例。 DatagramSocket 用于接收和发送UDP的Socket实例。该类有3个构造函数: DatagramSocket(): 通常用于客户端编程,它并没有特定监听的端口,仅仅使用一个临时的。程序会让操作系统分配一个可用的端口。 DatagramSocket(int port): 创建实例,并固定监听Port端口的报文。通常用于服务端 DatagramSocket(int port, InetAddress localAddr): 这是个非常有用的构建器,当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自LocalAddr的报文。 DatagramSocket具有的主要方法如下: receive(DatagramPacket d): 接收数据报文到d中。receive方法产生一个"阻塞"。“阻塞"是一个专业名词,它会产生一个内部循环,使程序暂停在这个地方,直到一个条件触发。 send(DatagramPacket dp): 发送报文dp到目的地。 setSoTimeout(int timeout): 设置超时时间,单位为毫秒。 close(): 关闭DatagramSocket。在应用程序退出的时候,通常会主动释放资源,关闭Socket,但是由于异常地退出可能造成资源无法回收。所以,应该在程序完成时,主动使用此方法关闭Socket,或在捕获到异常抛出后关闭Socket。 DatagramPacket 用于处理报文,它将Byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成Byte数组。应用程序在产生数据包是应该注意,TCP/IP规定数据报文大小最多包含65507个,通常主机接收548个字节,但大多数平台能够支持8192字节大小的报文。DatagramPacket类的构建器共有4个: DatagramPacket(byte[] buf, int length): 将数据包中Length长的数据装进Buf数组,一般用来接收客户端发送的数据。 DatagramPacket(byte[] buf, int offset, int length): 将数据包中从Offset开始、Length长的数据装进Buf数组。 DatagramPacket(byte[] buf, int length, InetAddress clientAddress, int clientPort): 从Buf数组中,取出Length长的数据创建数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。 ...

2015-08-24 · 2 min · 273 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= 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] 。 ...

2015-08-14 · 1 min · 119 words · -

java 字符串, 比较

java 字符串, 比较 java简单的字符串大小比较——compareTo()方法 在java编程中,我们会偶尔遇到字符串大小比较的问题,compareTo()方法很简单就实现这种功能。该方法用于判断一个字符串是大于、等于还是小于另一个字符串。判断字符串大小的依据是根据它们在字典中的顺序决定的。 语法: Str1.compareTo(Str2); 其返回的是一个int类型值。若Str1等于参数字符串Str2字符串,则返回0;若该Str1按字典顺序小于参数字符串Str2,则返回值小于0;若Str1按字典顺序大于参数字符串Str2,则返回值大于0。 java中的compareto方法,返回参与比较的前后两个字符串的asc码的差值,看下面一组代码 String a="a",b="b"; System.out.println(a.compareto.b); 则输出-1; 若a="a",b="a"则输出0; 若a="b",b="a"则输出1; 单个字符这样比较,若字符串比较长呢?? 若a=“ab”,b=“b”,则输出-1; 若a=“abcdef”,b=“b"则输出-1; 也就是说,如果两个字符串首字母不同,则该方法返回首字母的asc码的差值; 如果首字母相同呢?? 若a=“ab”,b=“a”,输出1; 若a=“abcdef”,b=“a"输出5; 若a=“abcdef”,b=“abc"输出3; 若a=“abcdef”,b=“ace"输出-1; 即参与比较的两个字符串如果首字符相同,则比较下一个字符,直到有不同的为止,返回该不同的字符的asc码差值,如果两个字符串不一样长,可以参与比较的字符又完全一样,则返回两个字符串的长度差值 参考: http://blog.sina.com.cn/s/blog_8a7200cd010104nx.html http://www.blogjava.net/hgc-ghc/archive/2013/03/28/397084.html //char 转String String str = String.valueOf(c); //判断字符大小写 if( Character.isUpperCase(c)) 1、字节数组转换为字符串 byte[] byBuffer = new byte[20]; … … String strRead = new String(byBuffer); strRead = String.copyValueOf(strRead.toCharArray(), 0, byBuffer.length]); 2、字符串转换成字节数组 byte[] byBuffer = new byte[200]; String strInput=“abcdefg”; byBuffer= strInput.getBytes(); ...

2015-08-13 · 1 min · 76 words · -

ConcurrentHashMap

ConcurrentHashMap ConcurrentHashMap 是一个线程安全的Hash Table,它的主要功能是提供了一组和HashTable功能相同但是线程安全的方法。ConcurrentHashMap可以做到读取数据不加锁,并 且其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,不用对整个ConcurrentHashMap加锁。 jdk1.7 由 Segment 数组、HashEntry 组成,和 HashMap 一样,仍然是数组加链表。 它的核心成员变量: /** * Segment 数组,存放数据时首先需要定位到具体的 Segment 中。 */ final Segment<K,V>[] segments; transient Set<K> keySet; transient Set<Map.Entry<K,V>> entrySet; Segment 是 ConcurrentHashMap 的一个内部类,主要的组成如下: static final class Segment<K,V> extends ReentrantLock implements Serializable { private static final long serialVersionUID = 2249069246763182397L; // 和 HashMap 中的 HashEntry 作用一样,真正存放数据的桶 transient volatile HashEntry<K,V>[] table; transient int count; transient int modCount; transient int threshold; final float loadFactor; } 和 HashMap 非常类似,唯一的区别就是其中的核心数据如 value ,以及链表都是 volatile 修饰的,保证了获取时的可见性。 ...

2015-08-12 · 8 min · 1510 words · -

A cycle was detected in the build path of project

A cycle was detected in the build path of project 解决Eclipse中Java工程间循环引用而报错的问题 如果我们的项目包含多个工程 (project) ,而它们之间又是循环引用的关系,那么Eclipse在编译时会抛出如下一个错误信息: “A cycle was detected in the build path of project: XXX” 解决方法非常简单: Eclipse Menu -> Window -> Preferences… -> Java -> Compiler -> Building -> Building path problems -> Circular dependencies -> 将Error改成Warning http://blog.csdn.net/kcai678/article/details/4668993

2015-08-10 · 1 min · 48 words · -

java.net.InetAddress

java.net.InetAddress http://www.cnblogs.com/hnrainll/archive/2012/01/09/2317515.html java.net.InetAddress类的使用 1.1. 简介 IP地址是IP使用的32位 (IPv4) 或者128位 (IPv6) 位无符号数字,它是传输层协议TCP,UDP的基础。InetAddress是Java对IP地址的封装,在java.net中有许多类都使用到了InetAddress,包括ServerSocket,Socket,DatagramSocket等等。 InetAddress的实例对象包含以数字形式保存的IP地址,同时还可能包含主机名 (如果使用主机名来获取InetAddress的实例,或者使用数字来构造,并且启用了反向主机名解析的功能) 。InetAddress类提供了将主机名解析为IP地址 (或反之) 的方法。 InetAddress对域名进行解析是使用本地机器配置或者网络命名服务 (如域名系统 (Domain Name System,DNS) 和网络信息服务 (Network Information Service,NIS) ) 来实现。对于DNS来说,本地需要向DNS服务器发送查询的请求,然后服务器根据一系列的操作,返回对应的IP地址,为了提高效率,通常本地会缓存一些主机名与IP地址的映射,这样访问相同的地址,就不需要重复发送DNS请求了。在java.net.InetAddress类同样采用了这种策略。在默认情况下,会缓存一段有限时间的映射,对于主机名解析不成功的结果,会缓存非常短的时间 (10秒) 来提高性能。 1.2. InetAddress对象的获取 InetAddress的构造函数不是公开的 (public) ,所以需要通过它提供的静态方法来获取,有以下的方法: static InetAddress[] getAllByName(String host) static InetAddress getByAddress(byte[] addr) static InetAddress getByAddress(String host,byte[] addr) static InetAddress getByName(String host) static InetAddress getLocalHost() 在这些静态方法中,最为常用的应该是getByName(String host)方法,只需要传入目标主机的名字,InetAddress会尝试做连接DNS服务器,并且获取IP地址的操作。代码片段如下,注意我们假设以下的代码,都是默认导入了java.net中的包,在程序的开头加上import java.net.*,否则需要指定类的全名java.net.InetAddress。 InetAddress address=InetAddress.getByName(“www.baidu.com”); 注意到这些方法可能会抛出的异常。如果安全管理器不允许访问DNS服务器或禁止网络连接,SecurityException会抛出,如果找不到对应主机的IP地址,或者发生其他网络I/O错误,这些方法会抛出UnknowHostException。所以需要写如下的代码: try { InetAddress address=InetAddress.getByName(“www.baidu.com”); System.out.println(address); } catch(UnknownHostException e) { e.printStackTrace(); } 下面是一则完整的例子: package org.dakiler.javanet.chapter1; import java.net.InetAddress; ...

2015-08-05 · 1 min · 153 words · -

java ByteBuffer

java ByteBuffer ByteBuffer 是 NIO 里用得最多的 Buffer, 它包含两个实现方式: HeapByteBuffer 是基于Java堆的实现, 而 DirectByteBuffer 则使用了 unsafe 的 API 进行了堆外的实现。这里只说 HeapByteBuffer。 Buffer 类 定义了一个可以线性存放primitive type数据的容器接口。Buffer主要包含了与类型 (byte, char…) 无关的功能。 值得注意的是Buffer及其子类都不是线程安全的。 每个Buffer都有以下的属性: capacity 这个Buffer最多能放多少数据。capacity一般在buffer被创建的时候指定。 limit 在Buffer上进行的读写操作都不能越过这个下标。当写数据到buffer中时,limit一般和capacity相等,当读数据时, limit代表buffer中有效数据的长度。 position 读/写操作的当前下标。当使用buffer的相对位置进行读/写操作时,读/写会从这个下标进行,并在操作完成后, buffer会更新下标的值。 mark 一个临时存放的位置下标。调用mark()会将mark设为当前的position的值,以后调用reset()会将position属性设 置为mark的值。mark的值总是小于等于position的值,如果将position的值设的比mark小,当前的mark值会被抛弃掉。 这些属性总是满足以下条件: 0 <= mark <= position <= limit <= capacity limit和position的值除了通过limit()和position()函数来设置,也可以通过下面这些函数来改变: Buffer clear() 把position设为0,把limit设为capacity,一般在把数据写入Buffer前调用。 Buffer flip() 把limit设为当前position,把position设为0,一般在从Buffer读出数据前调用。 Buffer rewind() 把position设为0,limit不变,一般在把数据重写入Buffer前调用。 Buffer 对象有可能是只读的, 这时, 任何对该对象的写操作都会触发一个 ReadOnlyBufferException isReadOnly()方法可以用来判断一个Buffer是否只读。 Buffer是一个抽象的基类 派生类: ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer ...

2015-08-05 · 4 min · 754 words · -

java transient

java transient http://www.cnblogs.com/lanxuezaipiao/p/3369962.html 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化, java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。 然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息 (如密码,银行卡号等) ,为了安全起见,不希望在网络操作 (主要涉及到序列化操作,本地序列化缓存也适用) 中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。 总之,java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。 transient 关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。 被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。 第三点可能有些人很迷惑,因为发现在User类中的username字段前加上static关键字后,程序运行结果依然不变,即static类型的username也读出来为"Alexia"了,这不与第三点说的矛盾吗?实际上是这样的: 第三点确实没错 (一个静态变量不管是否被transient修饰,均不能被序列化) ,反序列化后类中static型变量username的值为当前JVM中对应static变量的值,这个值是JVM中的不是反序列化得出的, 我们知道在Java中,对象的序列化可以通过实现两种接口来实现,若实现的是Serializable接口,则所有的序列化将会自动进行,若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。因此第二个例子输出的是变量content初始化的内容,而不是null。

2015-06-28 · 1 min · 21 words · -

Red-Black Tree,红黑树, R-B Tree

Red-Black Tree, 红黑树, R-B Tree https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/03.01.md 二叉查找树 由于红黑树本质上就是一棵二叉查找树,所以在了解红黑树之前,咱们先来看下二叉查找树。 二叉查找树 (Binary Search Tree) ,也称有序二叉树 (ordered binary tree) ,排序二叉树 (sorted binary tree) ,是指一棵空树或者具有下列性质的二叉树: 若任意结点的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若任意结点的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 任意结点的左、右子树也分别为二叉查找树。 没有键值相等的结点 (no duplicate nodes) 。 因为,一棵由n个结点,随机构造的二叉查找树的高度为lgn,所以顺理成章,一般操作的执行时间为O (lgn) . (至于n个结点的二叉树高度为lgn的证明,可参考算法导论 第12章 二叉查找树 第12.4节) 。 但二叉树若退化成了一棵具有n个结点的线性链后,则此些操作最坏情况运行时间为O (n) 。后面我们会看到一种基于二叉查找树-红黑树,它通过一些性质使得树相对平衡,使得最终查找、插入、删除的时间复杂度最坏情况下依然为O (lgn) 。 红黑树 前面我们已经说过,红黑树,本质上来说就是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。 但它是如何保证一棵n个结点的红黑树的高度始终保持在h = logn的呢?这就引出了红黑树的5条性质: 每个结点要么是红的,要么是黑的。 根结点是黑的。 每个叶结点 (叶结点即指树尾端NIL指针或NULL结点) 是黑的。 如果一个结点是红的,那么它的俩个儿子都是黑的。 对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。 正是红黑树的这5条性质,使得一棵n个结点是红黑树始终保持了logn的高度,从而也就解释了上面我们所说的"红黑树的查找、插入、删除的时间复杂度最坏为O(log n)“这一结论的原因。 红黑树是不符合AVL树的平衡条件的,即每个节点的左子树和右子树的高度最多差1的二叉查找树。但是提出了为节点增加颜色,红黑是用非严格的平衡来换取增删节点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决,而AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多。所以红黑树的插入效率更高!!! 这里引用一下知乎上的回答: Answer 1: 如果插入一个node引起了树的不平衡,AVL和RB-Tree都是最多只需要2次旋转操作,即两者都是O(1);但是在删除node引起树的不平衡时,最坏情况下,AVL需要维护从被删node到root这条路径上所有node的平衡性,因此需要旋转的量级O(logN),而RB-Tree最多只需3次旋转,只需要O(1)的复杂度。 其次,AVL的结构相较RB-Tree来说更为平衡,在插入和删除node更容易引起Tree的unbalance,因此在大量数据需要插入或者删除时,AVL需要rebalance的频率会更高。因此,RB-Tree在需要大量插入和删除node的场景下,效率更高。自然,由于AVL高度平衡,因此AVL的search效率更高。 map的实现只是折衷了两者在search、insert以及delete下的效率。总体来说,RB-tree的统计性能是高于AVL的。 作者: Acjx 链接: http://www.zhihu.com/question/20545708/answer/58717264 Answer 2 这个总结比较好: 红黑树的 查询性能略微逊色于AVL树,因为他比avl树会稍微不平衡最多一层,也就是说红黑树的查询性能只比相同内容的avl树最多多一次比较,但是,红黑树在插入和删除上完爆avl树, avl树每次插入删除会进行大量的平衡度计算,而红黑树为了维持红黑性质所做的红黑变换和旋转的开销,相较于avl树为了维持平衡的 开销要小得多 ...

2015-06-28 · 1 min · 148 words · -

Iterator, Enumeration

Iterator, Enumeration http://www.cnblogs.com/skywang12345/p/3311275.html Iterator和Enumeration区别 在Java集合中,我们通常都通过 “Iterator(迭代器)” 或 “Enumeration(枚举类)” 去遍历集合。今天,我们就一起学习一下它们之间到底有什么区别。 我们先看看 Enumeration.java 和 Iterator.java的源码,再说它们的区别。 Enumeration是一个接口,它的源码如下: package java.util; public interface Enumeration { boolean hasMoreElements(); E nextElement(); } Iterator也是一个接口,它的源码如下: package java.util; public interface Iterator { boolean hasNext(); E next(); void remove(); } 看完代码了,我们再来说说它们之间的区别。 (01) 函数接口不同 Enumeration只有2个函数接口。通过Enumeration,我们只能读取集合的数据,而不能对数据进行修改。 Iterator只有3个函数接口。Iterator除了能读取集合的数据之外,也能数据进行删除操作。 (02) Iterator支持fail-fast机制,而Enumeration不支持。 Enumeration 是JDK 1.0添加的接口。使用到它的函数包括Vector、Hashtable等类,这些类都是JDK 1.0中加入的,Enumeration存在的目的就是为它们提供遍历接口。Enumeration本身并没有支持同步,而在Vector、Hashtable实现Enumeration时,添加了同步。 而Iterator 是JDK 1.2才添加的接口,它也是为了HashMap、ArrayList等集合提供遍历接口。Iterator是支持fail-fast机制的: 当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。 第2部分 Iterator和Enumeration实例 下面,我们编写一个Hashtable,然后分别通过 Iterator 和 Enumeration 去遍历它,比较它们的效率。代码如下: 按 Ctrl+C 复制代码 按 Ctrl+C 复制代码 ...

2015-06-27 · 1 min · 77 words · -

ReferenceQueue

ReferenceQueue 引用队列 ReferenceQueue 使用 SoftReference,WeakReference,PhantomReference 的时候,可以关联一个 ReferenceQueue。那么当垃圾回收器准备回收一个被引用包装的对象时,该引用会被加入到关联的 ReferenceQueue。程序可以通过判断引用队列中是否已经加入引用, 来了解被引用的对象是否被GC回收。 作者: leilifengxingmw 链接: https://www.jianshu.com/p/6ae4f53a4752 来源: 简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 http://www.iflym.com/index.php/java-programe/201407140001.html 在java的引用体系中,存在着强引用,软引用,虚引用,幽灵引用,这4种引用类型。在正常的使用过程中,我们定义的类型都是强引用的,这种引用类型在回收中,只有当其它对象没有对这个对象的引用时,才会被GC回收掉。简单来说,对于以下定义: Object obj = new Object(); Ref ref = new Ref(obj); 在这种情况下,如果 ref 没有被 GC,那么 obj 这个对象肯定不会 GC 的。因为 ref 引用到了 obj。如果 obj 是一个大对象呢,多个这种对象的话,应用肯定一会就挂掉了。 那么,如果我们希望在这个体系中,如果obj没有被其它对象引用,只是在这个Ref中存在引用时,就把obj对象gc掉。这时候就可以使用这里提到的Reference对象了。 我们希望当一个对象被gc掉的时候通知用户线程,进行额外的处理时,就需要使用引用队列了。ReferenceQueue即这样的一个对象,当一个obj被gc掉之后,其相应的包装类,即ref对象会被放入queue中。我们可以从queue中获取到相应的对象信息,同时进行额外的处理。比如反向操作,数据清理等。 2 使用队列进行数据监控 一个简单的例子,通过往map中放入10000个对象,每个对象大小为1M字节数组。使用引用队列监控被放入的key的回收情况。代码如下所示: Object value = new Object(); Map<Object, Object> map = new HashMap<>(); for(int i = 0;i < 10000;i++) { byte[] bytes = new byte[_1M]; WeakReference<byte[]> weakReference = new WeakReference<byte[]>(bytes, referenceQueue); map.put(weakReference, value); } System.out.println("map.size->" + map.size()); 这里使用了weakReference对象,即当值不再被引用时,相应的数据被回收。另外使用一个线程不断地从队列中获取被gc的数据,代码如下: ...

2015-06-26 · 2 min · 262 words · -

CountDownLatch

CountDownLatch CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 CountDownLatch和CyclicBarrier的区别 (01) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。 (02) CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。 下面通过CountDownLatch实现: “主线程"等待"5个子线程"全部都完成"指定的工作(休眠1000ms)“之后,再继续运行。 import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; public class CountDownLatchTest1 { private static int LATCH_SIZE = 5; private static CountDownLatch doneSignal; public static void main(String[] args) { try { doneSignal = new CountDownLatch(LATCH_SIZE); // 新建5个任务 for(int i=0; i<LATCH_SIZE; i++) new InnerThread().start(); System.out.println("main await begin."); // "主线程"等待线程池中5个任务的完成 doneSignal.await(); System.out.println("main await finished."); } catch (InterruptedException e) { e.printStackTrace(); } } static class InnerThread extends Thread{ public void run() { try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " sleep 1000ms."); // 将CountDownLatch的数值减1 doneSignal.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } } } 主线程通过doneSignal.await()等待其它线程将doneSignal递减至0。其它的5个InnerThread线程,每一个都通过doneSignal.countDown()将doneSignal的值减1;当doneSignal为0时,main被唤醒后继续执行。 ...

2015-06-26 · 1 min · 97 words · -

JAVA的可变类与不可变类

JAVA的可变类与不可变类 可变类和不变类(Mutable and immutable class)的初步定义: 可变类: 当你获得这个类的一个实例引用时,你可以改变这个实例的内容。 不变类: 当你获得这个类的一个实例引用时,你不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。 如何创建一个自己的不可变类: .所有成员都是private .不提供对成员的改变方法,例如: setXXXX .确保所有的方法不会被重载。手段有两种: 使用final Class(强不可变类),或者将所有类方法加上final(弱不可变类)。 .如果某一个类成员不是原始变量(primitive)或者不可变类,必须通过在成员初始化(in)或者get方法(out)时通过深度clone方法,来确保类的不可变。 一个示例 import java.util.Date; public final class BrokenPerson { private String firstName; private String lastName; private Date dob; public BrokenPerson( String firstName, public BetterPerson( String firstName, String lastName, Date dob) String lastName, Date dob) { { this.firstName = firstName; this.firstName = firstName; this.lastName = lastName; this.lastName = lastName; this.dob = dob; //error this.dob = new Date( dob.getTime() ); //correct ...

2015-06-26 · 2 min · 359 words · -

临界区, 竞态条件

临界区, 竞态条件 临界区是程序中使用临界资源的一段程序 在同步的程序设计中,临界区 (Critical section) 指的是一个访问共享资源 (例如: 共享设备或是共享存储器) 的程序片段,而这些共享资源又无法同时被多个线程访问的特性。 当有线程进入临界区段时,其他线程或是行程必须等待 (例如: bounded waiting 等待法) ,有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共享资源是被异或的使用,例如: semaphore。 只能被单一线程访问的设备,例如: 打印机。 临界区 Critical section 保证在某一时刻只有一个线程能访问数据的简便方法,在任意时刻只允许一个线程对资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后,其他所有试图访问临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的. 临界区指的是一个访问共用资源 (例如: 共用设备或是共用存储器) 的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时, 其他线程或是进程必须等待 (例如: bounded waiting 等待法) ,有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用, 竞态条件与临界区 在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。如,同一内存区 (变量,数组,或对象) 、系统 (数据库,web services 等) 或文件。实际上,这些问题只有在一或多个线程向这些资源做了写操作时才有可能发生,只要资源没有发生变化,多个线程读取相同的资源就是安全的。 多线程同时执行下面的代码可能会出错: public class Counter { protected long count = 0; public void add(long value){ this.count = this.count + value; } } 想象下线程 A 和 B 同时执行同一个 Counter 对象的 add()方法,我们无法知道操作系统何时会在两个线程之间切换。JVM 并不是将这段代码视为单条指令来执行的,而是按照下面的顺序: 从内存获取 this.count 的值放到寄存器 ...

2015-06-11 · 1 min · 134 words · -

Runtime

Runtime http://blog.csdn.net/csh624366188/article/details/6684327 http://lavasoft.blog.51cto.com/62575/15565 Runtime.getRuntime()可以取得当前JVM的运行时环境,这也是在Java中唯一一个得到运行时环境的方法。 Runtime上其他大部分的方法都是实例方法,也就是说每次进行运行时调用时都要用到getRuntime方法。 Runtime中的exit方法是退出当前JVM的方法,估计也是唯一的一个吧,因为我看到System类中的exit实际上也是通过调用Runtime.exit()来退出JVM的,这里说明一下Java对Runtime返回值的一般规则 (后边也提到了) ,0代表正常退出,非0代表异常中止,这只是Java的规则,在各个操作系统中总会发生一些小的混淆。 Runtime.addShutdownHook()方法可以注册一个hook在JVM执行shutdown的过程中,方法的参数只要是一个初始化过但是没有执行的Thread实例就可以。 (注意,Java中的Thread都是执行过了就不值钱的哦) 说到addShutdownHook这个方法就要说一下JVM运行环境是在什么情况下shutdown或者abort的。文档上是这样写的,当最后一个非精灵进程退出或者收到了一个用户中断信号、用户登出、系统shutdown、Runtime的exit方法被调用时JVM会启动shutdown的过程,在这个过程开始后,他会并行启动所有登记的shutdown hook (注意是并行启动,这就需要线程安全和防止死锁) 。当shutdown过程启动后,只有通过调用halt方法才能中止shutdown的过程并退出JVM。 那什么时候JVM会abort退出那?首先说明一下,abort退出时JVM就是停止运行但并不一定进行shutdown。这只有JVM在遇到SIGKILL信号或者windows中止进程的信号、本地方法发生类似于访问非法地址一类的内部错误时会出现。这种情况下并不能保证shutdown hook是否被执行。 常见的应用 内存管理: Java提供了无用单元自动收集机制。通过totalMemory()和freeMemory()方法可以知道对象的堆内存有多大,还剩多少。 Java会周期性的回收垃圾对象 (未使用的对象) ,以便释放内存空间。但是如果想先于收集器的下一次指定周期来收集废弃的对象,可以通过调用gc()方法来根据需要运行无用单元收集器。一个很好的试验方法是先调用gc()方法,然后调用freeMemory()方法来查看基本的内存使用情况,接着执行代码,然后再次调用freeMemory()方法看看分配了多少内存。下面的程序演示了这个构想。 //此实例来自《java核心技术》卷一 class MemoryDemo{ public static void main(String args[]){ Runtime r = Runtime.getRuntime(); long mem1,mem2; Integer someints[] = new Integer[1000]; System.out.println(“Total memory is : " + r.totalMemory()); mem1 = r.freeMemory(); System.out.println(“Initial free is : " + mem1); r.gc(); mem1 = r.freeMemory(); System.out.println(“Free memory after garbage collection : " + mem1); ...

2015-06-01 · 2 min · 255 words · -

Java LockSupport, park unpark

“Java LockSupport, park unpark” LockSupport.park 调用后,线程状态是 WAITING LockSupport.park() 和 unpark(),与object.wait()和notify()的区别? 面向的主体不一样。LockSuport主要是针对Thread进行阻塞处理,可以指定阻塞队列的目标对象,每次可以指定具体的线程唤醒。Object.wait()是以对象为纬度,阻塞当前的线程和唤醒单个(随机)或者所有线程。 实现机制不同。虽然LockSuport可以指定monitor的object对象,但和object.wait(),两者的阻塞队列并不交叉。object.notifyAll()不能唤醒LockSupport的阻塞Thread. 阻塞和唤醒是对于线程来说的,LockSupport的park/unpark更符合这个语义,以"线程"作为方法的参数, 语义更清晰,使用起来也更方便。而wait/notify的实现使得"线程"的阻塞/唤醒对线程本身来说是被动的,要准确的控制哪个线程、什么时候阻塞/唤醒很困难, 要不随机唤醒一个线程 (notify) 要不唤醒所有的 (notifyAll) 。 LockSupport.park() (以下简称 park() ) 可能是 java.util.concurrent 包最重要的函数了,因为很多 java.util.concurrent 中的功能类都是利用 park() 来实现它们各自的阻塞。在 park() 之前 Java 也有过类似功能的函数——suspend(),相应的唤醒函数是 resume()。不过 suspend() 有个严重的问题是父线程有可能在调用 suspend() 之前子线程已经调用了 resume(),那么这个 resume() 并不会解除在它之后的 suspend(),因此父线程就会陷入永久的等待中。相比于 suspend(),park() 可以在以下几种情况解除线程的等待状态: 在 park() 前曾经调用过该线程的 unpark() 进而获得了一次"继续执行的权利",此时调用 park() 会立即返回,并且消耗掉相应的"继续执行的权利"。 在 park() 进入等待状态之后,有其他线程以该线程为目标调用 unpark()。 在 park() 进入等待状态之后,有其他线程以该线程为目标调用 interrupt()。 在 park() 进入等待状态之后,有可能会没有理由地解除等待状态。 (这也是为什么推荐在循环体中调用 park(),并在返回之后再次检查条件是否满足。) 其中第一条就可以保证 park() 不会遇到和 suspend() 同样的问题。 最简单的使用 park() 是这样的 ...

2015-05-25 · 5 min · 1023 words · -