SlideShare ist ein Scribd-Unternehmen logo
1 von 17
开锁项目技术总结



           hongjiang
           2009.9.3
com.alibaba.common.lang.enumeration.Enum
 为何会导致死锁 ?
JVM 规范:类初始化过程是唯一的。
当多个线程同时对某个类进行初始化时,只有一个线
  程能够进行了 clinit ,其他线程会检测并等待,当
  发现类已经被初始化完毕了它们不会再次执行
  clinit 。

简单的说,导致死锁的原因是因为线程 A 在对我们的
 Enum 中进行 clinit 时调用的方法要获取一个锁
 L ,而锁 L 正被线程 B 占用,同时线程 B 又正要访
 问这个 Enum 类成员,发现 Enum 类正在被线程
 A 初始化,于是线程 B 等待线程 A 。
   结果成了相互等待。
解决方式
将继承自
 com.alibaba.common.lang.enumeration.
 Enum
的枚举,改用 Java5 中的 enum 类型来实现。
Java5 中的 enum 关键字

用 enum 关键字定义的一个枚举类型并不是真
  的在 java 中创建了一种新的类型, enum
  类型本质上仍是一个 class

把它当作一种特殊的 class 来对待。
( 编译器在会将 enum 转换为 class)

在 C, C# 中 enum 类型都是作为值类型的,
  而 java 中 enum 是引用类型;
关于 Java.lang.Enum

Enum 是所有 Java 语言枚举类型的公共基本类
  :

public abstract class Enum<E extends Enum<E>>
     implements Comparable<E>, Serializable


Enum 被标记为 abstract ,但实际上并没有包含
  任何 abstract 方法。这是为何?
所使用的泛型表达式又如何理解?
关于 Java.lang.Enum


Enum 实现了 Comparable 和 Serializable 接口。

Enum 的构造函数是 protected 。
protected Enum(String name,int ordinal)

除了 toString 方法,其他方法都是 final 的。

用户不能自行继承 Enum 类,编译报错。只能通过
 enum 来定义枚举。
Java.lang.Enum 的 name() 方法
返回此枚举常量的名称,在其枚举声明中对其
 进行声明。 与此方法相比,大多数程序
 员应该优先考虑使用 toString() 方法
 ,因为 toString 方法返回更加用户友
 好的名称。该方法主要设计用于特殊情形
 ,其正确性取决于获取正确的名称,其名称
 不会随版本的改变而改变。
enum 中的静态方法 : valueOf,values
Java.lang.Enum 中包含:
public static <T extends Enum<T>> T valueOf(Class
   <T> enumType, String name)

对于每个继承 Enum 的实际枚举,编译器会自动增加一个静态的方法:
public static <T extends Enum<T>> T
   valueOf(String name)

相比 Enum 的 valueOf 少了第一个 enumType 参数,因为这个已经是
  确定的了。

values() 静态方法:这个静态方法 java.lang.Enum 中没有,是编译器
    增加到 enum 中的,返回枚举数组。
当 enum 出现在内部类中时:
public class MyClass{
  enum MyEnum{ A,B,C}
}

上面代码和 enum 前有 static 修饰符的效果
 是一样的。
enum 获取某个属性的方式
1) 定义该属性为成员变量,在构造函数中设置该属性,通过 getXXX
    方式来获取。 eg:
public enum OfferTypeEnum {
   /** 求购 */
   SALE("SALE"),

 /** 供应 */
 BUY("BUY"),

 /** 类型:合作 */
 MISC("MISC"),
  ……
 public String getName() {
  return this.name;
 }
2) Switch 方式判断。(容易维护)
   public enum OfferTypeEnum {
     SALE, BUY, MISC;

     public String getName() {
       switch(this){
           case SALE:
              return "SALE";
           case BUY:
              return "BUY";
           case MISC:
              return "MISC";
       }
       return "";
     }
   效率方面,第 2 种每次都需要比较,第 1 种采用空间换时间的方式
    。
   我们采用第一种方式。
将字符串解析为 enum
EnumUtil 工具类中的 getEnumByName ,等方法
  返回值都是旧的 Enum 类型。

对应的,增加了 JdkEnumUtil 工具类
使用 其中的 valueOf 函数来将字符串解析为新的枚
 举。

但对于那些枚举变量名和 name 属性不一致的情况,
 应该在相应的枚举中提供 parse 方法,比如:
OfferTypeEnum 中:

/** 类型:代理 */
AGENT(“AGEN”), // 这里是前人犯的错误,兼容以前的代码

需要该类自行提供一个解析方法:
public static OfferTypeEnum parse(String str){
  if(str == null) return null;
  if (str.equals("AGEN") || str.equals("AGENT"))
      return AGENT;
  return
  (OfferTypeEnum)JdkEnumUtil.valueOf(OfferTyp
  eEnum.class, str);
}
遇到继承的问题
旧的 Enum 有一些是有继承关系的,
一个实际的例子,比如:
TypeEnum 有两个子类
DangerousKeywordType
OfferCategoryEnum

可以将原先的父类改为 interface, enum 可以
 实现接口。
Java5 的 enum 为何不能继承?
                   会员类型:
                    免费会员



     中文站会员类型:                 国际站会员类型:
      Free+, 诚信通                 中供


Java5 的 enum 是不可以继承的,无法通过继承的方式来复用,因为它破坏
   了类型的一致性。
子类和父类类型不同。枚举的值必须是相同类型

enum 类型的 class.getModifiers() 是包含 final 修饰符的。
(编译器将 enum 转为 final class ,确保它无法继承。 )
toolkit-biz-command-jdk-enum

Toolkit-biz-command-jdk-enum 是宝宝用 Java5 枚
  举重写的一个包,不过考虑改动尽可能的
  小, Exodus2 中并没有使用这个包。

注意 ResultCodeUtil.java 中的 getName 方法
使用了 Enum 的 name() 方法,实际中有些枚举变量
 名称和 name 值是不同的,如果使用要避免使用
 ResultCodeUtil.getName 方法。
使用 shell 来检测重构后枚举名称是否写错


  Distill-trunk.sh    Distill-branch.sh       Javafile




    Checknew.sh      Checkunmatchedref.sh   Getunmatched.sh

Weitere ähnliche Inhalte

Mehr von wang hongjiang (18)

Shell,信号量以及java进程的退出
Shell,信号量以及java进程的退出Shell,信号量以及java进程的退出
Shell,信号量以及java进程的退出
 
中等创业公司后端技术选型
中等创业公司后端技术选型中等创业公司后端技术选型
中等创业公司后端技术选型
 
Scala类型系统
Scala类型系统Scala类型系统
Scala类型系统
 
Ali-tomcat
Ali-tomcatAli-tomcat
Ali-tomcat
 
functional-scala
functional-scalafunctional-scala
functional-scala
 
Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closures
 
Jvm内存管理基础
Jvm内存管理基础Jvm内存管理基础
Jvm内存管理基础
 
深入剖析Concurrent hashmap中的同步机制(上)
深入剖析Concurrent hashmap中的同步机制(上)深入剖析Concurrent hashmap中的同步机制(上)
深入剖析Concurrent hashmap中的同步机制(上)
 
聊一些电影
聊一些电影聊一些电影
聊一些电影
 
Java7 fork join framework and closures
Java7 fork join framework and closuresJava7 fork join framework and closures
Java7 fork join framework and closures
 
善用工具
善用工具善用工具
善用工具
 
Aswan&hump
Aswan&humpAswan&hump
Aswan&hump
 
Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析
 
Exodus重构和向apollo迁移
Exodus重构和向apollo迁移Exodus重构和向apollo迁移
Exodus重构和向apollo迁移
 
Exodus2 大局观
Exodus2 大局观Exodus2 大局观
Exodus2 大局观
 
Effective linux.3.(diagnosis)
Effective linux.3.(diagnosis)Effective linux.3.(diagnosis)
Effective linux.3.(diagnosis)
 
Effective linux.2.(tools)
Effective linux.2.(tools)Effective linux.2.(tools)
Effective linux.2.(tools)
 
Effective linux.1.(commandline)
Effective linux.1.(commandline)Effective linux.1.(commandline)
Effective linux.1.(commandline)
 

Enum开锁

  • 1. 开锁项目技术总结 hongjiang 2009.9.3
  • 2. com.alibaba.common.lang.enumeration.Enum 为何会导致死锁 ? JVM 规范:类初始化过程是唯一的。 当多个线程同时对某个类进行初始化时,只有一个线 程能够进行了 clinit ,其他线程会检测并等待,当 发现类已经被初始化完毕了它们不会再次执行 clinit 。 简单的说,导致死锁的原因是因为线程 A 在对我们的 Enum 中进行 clinit 时调用的方法要获取一个锁 L ,而锁 L 正被线程 B 占用,同时线程 B 又正要访 问这个 Enum 类成员,发现 Enum 类正在被线程 A 初始化,于是线程 B 等待线程 A 。 结果成了相互等待。
  • 4. Java5 中的 enum 关键字 用 enum 关键字定义的一个枚举类型并不是真 的在 java 中创建了一种新的类型, enum 类型本质上仍是一个 class 把它当作一种特殊的 class 来对待。 ( 编译器在会将 enum 转换为 class) 在 C, C# 中 enum 类型都是作为值类型的, 而 java 中 enum 是引用类型;
  • 5. 关于 Java.lang.Enum Enum 是所有 Java 语言枚举类型的公共基本类 : public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable Enum 被标记为 abstract ,但实际上并没有包含 任何 abstract 方法。这是为何? 所使用的泛型表达式又如何理解?
  • 6. 关于 Java.lang.Enum Enum 实现了 Comparable 和 Serializable 接口。 Enum 的构造函数是 protected 。 protected Enum(String name,int ordinal) 除了 toString 方法,其他方法都是 final 的。 用户不能自行继承 Enum 类,编译报错。只能通过 enum 来定义枚举。
  • 7. Java.lang.Enum 的 name() 方法 返回此枚举常量的名称,在其枚举声明中对其 进行声明。 与此方法相比,大多数程序 员应该优先考虑使用 toString() 方法 ,因为 toString 方法返回更加用户友 好的名称。该方法主要设计用于特殊情形 ,其正确性取决于获取正确的名称,其名称 不会随版本的改变而改变。
  • 8. enum 中的静态方法 : valueOf,values Java.lang.Enum 中包含: public static <T extends Enum<T>> T valueOf(Class <T> enumType, String name) 对于每个继承 Enum 的实际枚举,编译器会自动增加一个静态的方法: public static <T extends Enum<T>> T valueOf(String name) 相比 Enum 的 valueOf 少了第一个 enumType 参数,因为这个已经是 确定的了。 values() 静态方法:这个静态方法 java.lang.Enum 中没有,是编译器 增加到 enum 中的,返回枚举数组。
  • 9. 当 enum 出现在内部类中时: public class MyClass{ enum MyEnum{ A,B,C} } 上面代码和 enum 前有 static 修饰符的效果 是一样的。
  • 10. enum 获取某个属性的方式 1) 定义该属性为成员变量,在构造函数中设置该属性,通过 getXXX 方式来获取。 eg: public enum OfferTypeEnum { /** 求购 */ SALE("SALE"), /** 供应 */ BUY("BUY"), /** 类型:合作 */ MISC("MISC"), …… public String getName() { return this.name; }
  • 11. 2) Switch 方式判断。(容易维护)  public enum OfferTypeEnum {  SALE, BUY, MISC;  public String getName() {  switch(this){  case SALE:  return "SALE";  case BUY:  return "BUY";  case MISC:  return "MISC";  }  return "";  }  效率方面,第 2 种每次都需要比较,第 1 种采用空间换时间的方式 。  我们采用第一种方式。
  • 12. 将字符串解析为 enum EnumUtil 工具类中的 getEnumByName ,等方法 返回值都是旧的 Enum 类型。 对应的,增加了 JdkEnumUtil 工具类 使用 其中的 valueOf 函数来将字符串解析为新的枚 举。 但对于那些枚举变量名和 name 属性不一致的情况, 应该在相应的枚举中提供 parse 方法,比如:
  • 13. OfferTypeEnum 中: /** 类型:代理 */ AGENT(“AGEN”), // 这里是前人犯的错误,兼容以前的代码 需要该类自行提供一个解析方法: public static OfferTypeEnum parse(String str){ if(str == null) return null; if (str.equals("AGEN") || str.equals("AGENT")) return AGENT; return (OfferTypeEnum)JdkEnumUtil.valueOf(OfferTyp eEnum.class, str); }
  • 14. 遇到继承的问题 旧的 Enum 有一些是有继承关系的, 一个实际的例子,比如: TypeEnum 有两个子类 DangerousKeywordType OfferCategoryEnum 可以将原先的父类改为 interface, enum 可以 实现接口。
  • 15. Java5 的 enum 为何不能继承? 会员类型: 免费会员 中文站会员类型: 国际站会员类型: Free+, 诚信通 中供 Java5 的 enum 是不可以继承的,无法通过继承的方式来复用,因为它破坏 了类型的一致性。 子类和父类类型不同。枚举的值必须是相同类型 enum 类型的 class.getModifiers() 是包含 final 修饰符的。 (编译器将 enum 转为 final class ,确保它无法继承。 )
  • 16. toolkit-biz-command-jdk-enum Toolkit-biz-command-jdk-enum 是宝宝用 Java5 枚 举重写的一个包,不过考虑改动尽可能的 小, Exodus2 中并没有使用这个包。 注意 ResultCodeUtil.java 中的 getName 方法 使用了 Enum 的 name() 方法,实际中有些枚举变量 名称和 name 值是不同的,如果使用要避免使用 ResultCodeUtil.getName 方法。
  • 17. 使用 shell 来检测重构后枚举名称是否写错 Distill-trunk.sh Distill-branch.sh Javafile Checknew.sh Checkunmatchedref.sh Getunmatched.sh

Hinweis der Redaktion

  1. 1 )为何 Enum 声明为 abstract ,我也不很清楚。可能是限制我们对 Enum 的使用,也可能是给编译器用的,类似于某些标记接口。 2 )这个泛型表达式表示 泛型参数型必须是 Enum 泛型类的子类,这个地方要看看 java 规范,了解的不深。 弄清楚关于泛型的几个术语: 1. Type Parameters 2. Type Variables 3. Type Arguments 4. Parameterized Types 5. Bounds