1 什么是可见性,再说说volatile变量
JVM内存模型时总结了
2 什么是对象的发布与逸出?
- 发布(Publish):一个对象是指,使对象能够在当前作用域之外的代码中使用。
- 逸出(Escape):是一种错误的发布, 当一个对象还没有构造完成时 , 就使得它被其他线程看见。
3 发布对象的几种情形
- 将对象的引用保存到一个公有的静态变量中
- 如果一个已经发布的对象能够通过非私有的变量引用和方法调用到达其他对象,那么这些对象也都会发布
- 发布一个内部类实例
4 如何安全的构造对象?
不要在构造工程中使this引用溢出,需要遵循
- 不要在构造函数中启动一个线程
- 不要在构造函数中调用一个可改写的实例方法(既不是私有方法,也不是终结方法)
5 线程封闭有哪几种方式?
- Ad-hoc线程封闭:维护线程封闭性的职责完全由程序实现来承担。
- 栈封闭:栈封闭是线程封闭的特例,在栈封闭中,只有通过局部变量才能访问对象。局部变量的固有属性之一就是封闭在执行线程中。他们位于执行线程的栈中,其他线程无法访问这个栈。
- ThreadLocal: ThreadLocal是一个本地线程副本变量工具类。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。
6 谈谈你对ThreadLocal的理解
参考简书:ThreadLocal 原理
ThreadLocal类中定义了一个内部类ThreadLocalMap,ThreadLocalMap中有一个Entry的静态内部类。并且ThreadLocalMap中有private Entry[] table;用于保存变量值。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry就是存变量值的地方。而在Thread类中,有如下字段
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
所以我们发现每个线程都有一个ThreadLocalMap,该线程的ThreadLocalMap存储着自己的变量副本。ThreadLocal只是帮我们维护这些变量副本。下面我们看看ThreadLocal的set方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
在为当前线程设置值时,首先获取当前线程Thread.currentThread(); 再获取当前线程的|ThreadLocalMap,再给当前线程的map进行设置和初始化。
我们再看ThreadLocal的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();
}
还是先获取当前线程类,再获取当前线程的map,通过map.getEntry(this);拿到值,如果是空的就通过setInitialValue()初始化一个Entry并返回空值。
不可变对象,不可变对象满足的条件?
定义:如果某个对象在被创建后其状态就不能被修改,那么这个对象就称为不可变对象。
7 不可变对象一定是线程安全的。当满足以下条件时,对象才是不可变的:
- 对象创建以后其状态就不能修改
- 对象的所有域都是final类型
- 对象是正确创建的(在对象的创建期间,this引用没有逸出)
8 如何安全的发布对象?
要安全的发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见。一个正确构造的对象可以通过以下方式来安全地发布:
- 在静态初始化函数中初始化下一个对象引用
- 将对象的引用保存到volatile类型的域或者AtomicReferance对象中
- 将对象的引用保存到某个正确构造对象的final类型域中
- 将对象的引用保存到一个由锁保护的域中
9 不可变对象,事实不可变对象,可变对象如何发布?
不可变对象:如果某个对象在被创建后其状态就不能被修改,那么这个对象就称为不可变对象。
事实不可变对象:如果对象从技术上来说是可变的,但其状态在发布后不会再改变,那么把这种对象称为“事实不可变对象(Effectively Immutable Object)”
对于这三类对象,可以通过如下方式安全发布:
- 不可变对象可以通过任意机制来发布
- 事实不可变对象必须通过安全方式来发布
- 可变对象必须通过安全方式来发布,并且必须是线程安全的或者由某个锁保护起来
10 如何安全的共享对象?
- 线程封闭。线程封闭的对象只能由一个线程拥有,对象被封闭在该线程中,并且只能由这个线程修改。
- 只读共享。在没有额外同步的情况下,共享的只读对象可以由多个线程并发访问,但任何线程都不能修改它。共享的只读对象包括不可变对象和事实不可变对象。
- 线程安全共享。线程安全的对象在其内部实现同步,因此多个线程可以通过对象的公有接口来进行访问而不需要进一步同步。
- 保护对象。被保护的对象只能通过持有特定的锁来访问。保护对象包括封装在其他线程安全对象中的对象,以及发布的并且由某个特定锁保护的对象。