java并发编程(二)对象的共享

来源:互联网 发布:电子琴教学软件 编辑:程序博客网 时间:2024/05/16 15:02

接昨天   《java并发编程(一)线程安全性》  

发布和逸出:

“publish”,发布一个对象的意思是:使对象能够在当前作用域之外的代码中使用。(Publishingan object means making it available to code outside of its current scope )。发布内部状态可能破坏封装性,并使得程序难以维系不变性条件。如果对象在构造完成前就发布该对象,就会破坏线程安全性。当某个不应该发布的对象被发布时,被称为“逸出”。
public static Set<Secret> know;public void init() {know = new HashSet<>();}
当发布某个对象时,可能会间接的发布其他对象。如果将一个Secret对象添加到集合know中,那么同样会发布这个对象,因为任何代码都可以便利这个集合,并获得对这个心Secret对象的引用。同样从非私有方法返回一个引用,那么同样会发布返回的对象。
private String[] states = new String[] { "AK", "AL" };public String[] getState() {return states;}
代码发布了一个本是私有的状态数组,逸出了他所在的作用域,任何一个调用着都能修改这个数组的内容。封装的主要原因是:封装能够对程序的正确性进行分析,并使无意破坏设计约束条件能难。(it makesit practical to analyze programs for correctness and harder to violate design con-straints accidentally. 

还有一种发布对象或其内部状态的机制就是发布一个内部的类实例。看例子

</pre><div><span style="white-space: pre;"></span><pre name="code" class="java">public class ThisEscape {private String name = null;public ThisEscape(EventSource eventSource) {eventSource.registerListener(new EventListener() {@Overridepublic void onEvent() {System.out.println(name.toString());}});name = "23";}}
我们在构造函数中,发布EventListener时,同时也发布了ThisEscape,也就是说,onEvent 中的test方法使用了 ThisEscape对象中的东西,而这时 ThisEscape 并没有完成创建。通俗的讲 就是 在构造函数中调用了一个可改写的实例方法。
如果想在构造函数中注册一个事件监听或者启动线程,可以使用一个私有的构造函数,和一个公共的工厂方法
看书中的例子:
public class SafeListener {private final EventListener eventListener;<span style="color:#ff0000">private</span> SafeListener(EventSource eventSource) {eventListener = new EventListener() {@Overridepublic void onEvent() {}};}public static SafeListener newInstance(EventSource eventSource) {SafeListener listener = new SafeListener(eventSource);eventSource.registerListener(listener.eventListener);return listener;}}
 看到newInstance 突然想想到两点 :
1.以前做过一个android 项目,引入的一个第三方框架,好像也是这样写的,当时只知道用,不懂为什么。有时间一定补上
2.newInstance 让我想起了 反射 ,赶紧翻看了下Class 源码,并分析。
    <span style="color:#ff0000">private</span> Class() {}
同样,不让自己创建对象,并且带有说明

    /*

     * Constructor. Only the Java Virtual Machine creates Class

     * objects.

     */

只有虚拟机才能创建Class对象,Class 类中static方法不是很多,我们要得到一个字节码只能通过Class.forName,这是不是也是为了防止逸出呢? 请大牛指教。
   public static Class<?> forName(String className)                throws ClassNotFoundException {        return forName0(className, true,                        ClassLoader.getClassLoader(Reflection.getCallerClass()));    }

线程封闭

栈封闭


ThreadLocal类

ThreadLocal类保存的是 线程的标示和要保存的值。查看ThreadLocal源码就能明白原理。
public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }

set方法首先得到当前线程,然后通过传入的对象,取得Map集合,如果map为空就创建,不为空就更新。
同样的 get方法 我们也大约能猜到是什么样的,请看
    public T get() {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null) {                @SuppressWarnings("unchecked")                T result = (T)e.value;                return result;            }        }        return setInitialValue();    }

在现实应用程序框架大量的运用了ThreadLocal ,我觉得浏览器在访问服务器时候就可以通过 ThreadLocal来区分客户端。因为 只要浏览器访问服务器 就会开启一个线程。

不可变性:

当一个对象不可变,那他一定是线程安全的。
不可变对象必须包括三点:
1.对象创建后其状态就不能改变。
2.对象的所有域都是fnal类型。
3.对象是正确的创建的。(在对象创建时期,this引用没有逸出)
final关键字修饰的变量 引用不可变,但是对象依然可以被修改。java中,final域能保证初始化过程的安全性。
一个编程的习惯:除非需要更高的可见性,否则应将所有的域都声明为私有域,除非某个域是可见的,否则将他声明为final域。

安全性发布的常用模式:

可变对象必须通过安全方式来发布,这就意味着发布和使用该对象的线程时都必须同步。要安全地发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见。以下方式可以保证安全的发布:
1.在静态初始化函数中初始化一个对象的引用。
2.将对象的引用保存到volatile类型的域活着AtomicReference对象中。
3.将对象的引用保存到某个正确构造对象的final类型域中。
4.将对象的引用保存到一个由锁保护的域中。

0 0
原创粉丝点击