java并发编程实战

3 对象的共享

2018-09-11  本文已影响3人  史小猿

3.1可见性

加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步

Volatile变量

当把变量定义为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者其他处理器不可见的地方,因此在读取volatile类型的变量总是会返回最新写入的值。

写入volatile变量相当于退出同步代码块,读取volatile变量相当于进入同步代码块

加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。
当且仅当满足以下所有条件时,才应该使用volatile变量:

3.2发布与逸出
隐式this逸出和安全的对象构造的例子

/**
 * ThisEscape
 * <p/>
 * Implicitly allowing the this reference to escape
 *
 * @author Brian Goetz and Tim Peierls
 */
public class ThisEscape {
    public ThisEscape(EventSource source) {
        source.registerListener(new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        });
    }

    void doSomething(Event e) {
    }


    interface EventSource {
        void registerListener(EventListener e);
    }

    interface EventListener {
        void onEvent(Event e);
    }

    interface Event {
    }
}

利用final和工厂来防止this逸出


/**
 * SafeListener
 * <p/>
 * Using a factory method to prevent the this reference from escaping during construction
 *
 * @author Brian Goetz and Tim Peierls
 */
public class SafeListener {
    private final EventListener listener;

    private SafeListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        };
    }

    public static SafeListener newInstance(EventSource source) {
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }

    void doSomething(Event e) {
    }


    interface EventSource {
        void registerListener(EventListener e);
    }

    interface EventListener {
        void onEvent(Event e);
    }

    interface Event {
    }
}

不过本人看了这个,还是不懂咋回事,不懂为什么this逸出了,参考了博客写了下边的例子就知道咋回事了

public class ThisEscape {
   private String name = null;

   public ThisEscape(EventSource source) {
       source.registerListener(new EventListener() {
           public void onEvent(Event e) {
               doSomething(e);
           }
       });
       name = "TEST";
   }

   void doSomething(Event e) {
       System.out.println(this.name.toString());
   }

   interface EventSource {
       void registerListener(EventListener e);
   }

   interface EventListener {
       void onEvent(Event e);
   }

   interface Event {
   }

   public static void main(String[] args) {
       EventSource e = new EventSource() {
           public void registerListener(EventListener e) {
               e.onEvent(null);
           }
       };
       new ThisEscape(e);
   }
}

执行结果


public class SafeListener {
    private final EventListener listener;
    private String name = null;

    private SafeListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        };
        name = "TEST";
    }

    public static SafeListener newInstance(EventSource source) {
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }

    void doSomething(Event e) {
        System.out.println(this.name.toString());
    }
    interface EventSource {
        void registerListener(EventListener e);
    }

    interface EventListener {
        void onEvent(Event e);
    }

    interface Event {
    }
    public static void main(String[] args) throws InterruptedException {
        EventSource es = new EventSource(){
            public void registerListener(EventListener e){
                e.onEvent(null);
            }
        };
        //        new ThisEscape(es);
        SafeListener.newInstance(es);
    }


}

执行结果



第一个例子,类没有初始化完全,就使用this 报了空指针异常,this逸出。
第二个例子,类完全初始化后,使用this 正常打印。

3.3线程封闭(todo)
例子 JDBC Connection,Swing
栈封闭
引用被封闭在了方法中
ThreadLocal类

上一篇 下一篇

猜你喜欢

热点阅读