java

位置:IT落伍者 >> java >> 浏览文章

Java线程模型缺陷研究[6]


发布日期:2023年02月26日
 
Java线程模型缺陷研究[6]

访问的问题

如果缺少良好的访问控制会使线程编程非常困难大多数情况下如果能保证线程只从同步子系统中调用不必考虑线程安全(threadsafe)问题我建议对 Java 编程语言的访问权限概念做如下限制应精确使用 package 关键字来限制包访问权我认为当缺省行为的存在是任何一种计算机语言的一个瑕疵我对现在存在这种缺省权限感到很迷惑(而且这种缺省是包(package)级别的而不是私有(private)在其它方面Java 编程语言都不提供等同的缺省关键字虽然使用显式的 package 的限定词会破坏现有代码但是它将使代码的可读性更强并能消除整个类的潜在错误 (例如如果访问权是由于错误被忽略而不是被故意忽略) 重新引入 private protected 它的功能应和现在的 protected 一样但是不应允许包级别的访问 允许 private private 语法指定实现的访问对于所有外部对象是私有的甚至是当前对象是的同一个类的对于左边的唯一引用(隐式或显式)应是 this 扩展 public 的语法以授权它可制定特定类的访问例如下面的代码应允许 Fred 类的对象可调用 some_method() 但是对其它类的对象这个方法应是私有的

这种建议不同于 C++ 的 friend 机制friend 机制中它授权一个类访问另一个类的所有 私有部分在这里我建议对有限的方法集合进行严格控制的访问用这种方法一个类可以为另一个类定义一个接口而这个接口对系统的其余类是不可见的

除非域引用的是真正不变(immutable)的对象或static final 基本类型否则所有域的定义应是 private 对于一个类中域的直接访问违反了 OO 设计的两个基本规则抽象和封装从线程的观点来看允许直接访问域只使对它进行非同步访问更容易一些

增加$property 关键字带有此关键字的对象可被一个bean 盒应用程序访问这个程序使用在 Class 类中定义的反射操作(introspection) API否则与 private private 同效 $property 属性可用在域和方法这样现有的 JavaBean getter/setter 方法可以很容易地被定义为属性

不变性(immutability)

由于对不变对象的访问不需要同步所以在多线程条件下不变的概念(一个对象的值在创建后不可更改)是无价的Java 编程言语中对于不变性的实现不够严格有两个原因对于一个不变对象在其被未完全创建之前可以对它进行访问这种访问对于某些域可以产生不正确的值 对于恆定 (类的所有域都是 final) 的定义太松散对于由 final 引用指定的对象虽然引用本身不能改变但是对象本身可以改变状态

第一个问题可以解决不允许线程在构造函数中开始执行 (或者在构造函数返回之前不能执行开始请求)

对于第二个问题通过限定final 修饰符指向恆定对象可以解决此问题这就是说对于一个对象只有所有的域是 final并且所有引用的对象的域也都是 final此对象才真正是恆定的为了不打破现有代码这个定义可以使用编译器加强即只有一个类被显式标为不变时此类才是不变类

有了$immutable 修饰符后在域定义中的 final 修饰符是可选的

最后当使用内部类(inner class)后在 Java 编译器中的一个错误使它无法可靠地创建不变对象

既使空的 final 在每个构造函数中都有初始化还是会出现这个错误信息自从在 版本中引入内部类后编译器中一直有这个错误在此版本中(三年以后)这个错误依然存在现在该是改正这个错误的时候了

对于类级域的实例级访问

除了访问权限外还有一个问题即类级(静态)方法和实例(非静态)方法都能直接访问类级(静态)域这种访问是非常危险的因为实例方法的同步不会获取类级的锁所以一个synchronized static 方法和一个 synchronized 方法还是能同时访问类的域改正此问题的一个明显的方法是要求在实例方法中只有使用 static 访问方法才能访问非不变类的 static 域当然这种要求需要编译器和运行时间检查

由于f() 和 g() 可以并行运行所以它们能同时改变 x 的值(产生不定的结果)请记住这里有两个锁 static 方法要求属于 Class 对象的锁而非静态方法要求属于此类实例的锁

或则编译器应获得读/写锁的使用

另外一种方法是(这也是一种理想的 方法) 编译器应 自动 使用一个读/写锁来同步访问非不变 static 域这样程序员就不必担心这个问题

[] [] [] [] [] [] []

               

上一篇:Java线程模型缺陷研究[7]

下一篇:破除Java神话之线程按优先级唤醒