这是第2章和第3章的读书笔记
1.对象的状态与引用
类拥有:①数据成员②方法成员
其中数据成员又被称为域,域又分:①静态域②非静态域/实例变量
其中静态域为类以及所有的类的实例所共有,而实例变量为某个类的实例所独有。
而一个对象,包含对象的引用和对象的状态。
这个对象的状态就是说,对象域的具体数据,以及依赖对象的域的数据。
2.线程安全
当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,
并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
3.线程安全的3种方法
(1)不再线程间共享数据
线程封闭
(2)使用不可变的状态
不可变性
(3)同步状态
①Volitile
②Synchronized
③ExplicitLock
④AtomicVariable
注意:同步状态包含,①原子性②可见性
两层意思,而Volitile只能保证可见性。
4.发布与逸出
发布:使对象能够在当前作用域之外的代码中使用。
逸出:当一个不应该被发布的对象被发布了。
逸出的几种方式:
(1)将对象的引用保存到一个共有的静态变量中。
(2)从非私有方法返回一个引用。
(3)把一个对象传递给某个外部方法。
(4)发布一个内部的类实例(在这个类实例中,可能会通过隐式的this,从而访问到外部类的一些方法或者域)。
注意:关于这个(4),我的理解是,一个内部类能在任何时候通过自身的方法访问到外部类的域,即使外部类仍然在构建之中。
那么如果在构造函数之中将一个内部类对象发布出去,别的线程就可能通过这个内部类对象访问到外部类对象的不一致状态。这是一个不应该的发布。
不过,这是建立在此内部类对象的某个方法访问了外部类域的基础上,如果它的方法均没有访问外部类的成员,那么能不能通过反射或者什么方法访问外部类的成员我现在还不知道?
5.如何正确地发布
(1)在静态初始化环境中,初始化一个对象引用
比如,public static Holder holder = new Holder(42);
(2)将对象的引用保存到volatile类型的域或者AtomicReferance对象中
(3)将对象的引用保存到某个正确构造对象的final类型域中
(4)将对象的引用保存到一个由锁保护的域中
比如,将对象放入一个线程安全的容器之中。
6.其他
(1)封装的作用
①封装有利于对于线程状态的掌控,有利于线程安全。
②封装有可能不利于程序的性能。
原则:首先保证程序的正确性与可维护性,在必要的时候才为了性能一定程度上打破封装。
(2)不正确的发布
由于发布的时候,没有采用必要的同步手段。
就算对象是正确构造的,并且放入了一个公共域。但是由于存在可见性问题,仍然可能造成对象不完整。
(3)不同对象的发布方法
①不可变对象(A。状态不可变B。所有域都是final域C。正确的构造过程)可以通过任意机制来发布
②事实不可变对象必须通过安全方式来发布
③可变对象必须通过安全方式来发布,并且必须是线程安全的或者由某个锁保护起来。