文档章节

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

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

© 著作权归作者所有

共有 人打赏支持
呆萌的我
粉丝 4
博文 15
码字总数 15443
作品 0
天津
私信 提问
基于JVM原理、JMM模型和CPU缓存模型深入理解Java并发编程

许多以Java多线程开发为主题的技术书籍,都会把对Java虚拟机和Java内存模型的讲解,作为讲授Java并发编程开发的主要内容,有的还深入到计算机系统的内存、CPU、缓存等予以说明。实际上,在实...

leoliu168
11/08
0
0
JVM内存结构 VS Java内存模型 VS Java对象模型

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

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

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

MageekChiu
01/10
0
0
并发编程 ThreadLocal源码分析

引入ThreadLocal的场景 最近在写项目的时候自己也有疑虑,因为这个项目涉及到爬虫,使用的WebMagic这款垂直爬虫框架 垂直式爬虫 垂直型爬虫关注内容与准确还有效率.比较常见的就是舆情项目,...

红色迷宫
07/28
0
0
Java并发编程原理与实战二十五:ThreadLocal线程局部变量的使用和原理

1.什么是ThreadLocal ThreadLocal顾名思义是线程局部变量。这种变量和普通的变量不同,这种变量在每个线程中通过get和set方法访问, 每个线程有自己独立的变量副本。线程局部变量不存在多个线...

pony1223
08/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

linux-scp 远程拷贝报错原因

刚拿到一台重装后的服务器,远程ssh都正常,但是一scp拷贝东西就报错: 本地确定是有scp命令的,而且如果是本地没有scp不会报后面那句lost connection,因此就是远程没有scp这个命令。因此在...

linuxprobe16
13分钟前
0
0
OSChina 周六乱弹 —— 谁小时候没当过熊孩子呀

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @小小编辑:推荐歌曲《行尸走肉》- amazarashi 《行尸走肉》- amazarashi 手机党少年们想听歌,请使劲儿戳(这里) @神话 :周五了,周末干啥...

小小编辑
37分钟前
23
1
docker部署springboot项目

安装docker 菜鸟教程 springboot项目 maven依赖 <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001......

yimingkeji
今天
14
0
1: Cordova 配置WebView可以打开外部链接

一、问题:在使用Cordova生成的Android App中默认情况下WebView中的超链接,如果不是相对链接,会默认使用浏览器打开。 如果想用默认webview打开 解决方案:修改config.xml文件添加链接配置节...

wecloudnet
今天
1
0
Beetl介绍以及集成SpringBoot2.0 ---《Beetl视频课程》(1)

目的:引导阅读官方文档 目标:实现一个自己的博客 一、Beetl介绍 Beetl目前版本是2.9.3,相对于其他java模板引擎,具有功能齐全,语法直观,性能超高,以及编写的模板容易维护等特点。使得开发...

Gavin-King
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部