文档章节

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

呆萌的我
 呆萌的我
发布于 2015/10/13 20:11
字数 1358
阅读 4
收藏 0

接昨天   《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() {

			@Override
			public 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() {
			@Override
			public 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.将对象的引用保存到一个由锁保护的域中。

版权声明:本文为博主原创文章,未经博主允许不得转载。

© 著作权归作者所有

共有 人打赏支持
呆萌的我
粉丝 3
博文 15
码字总数 15443
作品 0
天津
JVM内存结构 VS Java内存模型 VS Java对象模型

Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点。而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚。比如本文我们要讨论的JVM内存结构、Java内存模型和...

Java架构
07/11
0
0
计算机科学中抽象的好处与问题—伪共享实例分析

David John Wheeler有一句名言“计算机科学中的任何问题都可以通过加上一层间接层来解决”,一层不够就再加一层。后半句是我加的 (* ̄︶ ̄) ,虽然有点玩笑的意思,但是也的确能说明一些问题...

MageekChiu
01/10
0
0
Java 使用 happen-before 规则实现共享变量的同步操作

前言 熟悉 Java 并发编程的都知道,JMM(Java 内存模型) 中的 happen-before(简称 hb)规则,该规则定义了 Java 多线程操作的有序性和可见性,防止了编译器重排序对程序结果的影响。按照官方的...

stateIs0
01/20
0
0
(三)Java并发学习笔记--线程封闭

线程封闭 实现好的并发是一件困难的事情,所以很多时候我们都想躲避并发。避免并发最简单的方法就是线程封闭。什么是线程封闭呢? 就是把对象封装到一个线程里,只有这一个线程能看到此对象。...

潘天涯
08/14
0
0
结合Java数据类型分析JVM运行时数据结构

阅读建议:本博客基于《Java编程思想》、《深入理解Java虚拟机》、《java并发编程实战》三本Java书籍和面试中遇到的问题而做的总结。 JVM(JAVA Virtual Machine Java虚拟机)运行时数据区域:...

硕士鸭
2017/10/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Bash重定向详解

Bash重定向详解 Bash的重定向指的是将命令的输入和输出导向不同地方,而不是默认的标准输入、标准输出和标准错误。Bash的重定向实际上是对标准输入、标准输出和标准错误的重置,进而将所需输...

小陶小陶
今天
3
0
EventBus原理深度解析

一、问题描述 在工作中,经常会遇见使用异步的方式来发送事件,或者触发另外一个动作:经常用到的框架是MQ(分布式方式通知)。如果是同一个jvm里面通知的话,就可以使用EventBus。由于Event...

yangjianzhou
今天
6
0
OpenCV图像处理实例:libuv+cvui显示摄像头视频

#include <iostream>#include <opencv2/opencv.hpp>#define CVUI_IMPLEMENTATION#include <cvui.h>extern "C"{#include <uv.h>}using namespace std;#define WINDOW_NAM......

IOTService
今天
3
0
openJDK之JDK9的String

1.openJDK8的String 先来看下openJDK8的String的底层,如下图1.1所示: 图1.1 底层上使用的是char[],即char数组 每个char占16个bit,Character.SIZE的值是16。 2.openJDK9中的String 图2.1...

克虏伯
今天
4
0
UEFI 模式下如何安装 Ubuntu 16.04

作者:知乎用户 链接:https://www.zhihu.com/question/52092661/answer/259583475 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 针对UEFI模式下安装U...

寻知者
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部