(十二)volatile变量
volatile变量 ,用来确保将变量的更新操作通知到其他线程。当把变量申明为volatile类型后,编译器与运行时都会注意到这个变量是共享的。因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型时总会返回最新写入的值。
仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它们。如果在验证正确性时需要对可见性进行复杂的判断,那么就不要使用volatile变量。volatile变量的正确使用方式包括:确保它们自身状态的可见性,确保它们所引用对象的状态的可见性,以及标识一些重要的程序生命周期事件的发生(例如,初始化或关闭)。
一个典型用法:
检查某个状态标记以判断是否退出循环。示例中,线程试图通过类似于数绵羊的传统方法进入休眠状态,asleep必须为volatile变量,否则,当asleep被另一个线程修改时,执行判断的线程却发现不了。
volatile boolean asleep;
……
while(!asleep)
countSomeSheep();
volatile变量通常用做某个操作完成,发生中断或者状态的标志。volatile的语义不足以确保递增操作(count++)的原子性,除非你能确保只有一个线程对变量执行写操作。
加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。
(十三) ThreadLocal :
http://www.cnblogs.com/dolphin0520/p/3920407.html
http://www.iteye.com/topic/103804
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。
另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。
如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。
ThreadLocal是如何为每个线程创建变量的副本的:
首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。
初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
ThreadLocal的应用:
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
(十四) 不可变对象
如果某个对象在被创建后其状态就不能被修改,那么这个对象就称为不可变对象。不可变对象一定是线程安全的。
不可变性的所有需求:状态不可修改,所有域都是final类型,以及正确的构造过程。
任何线程都可以在不需要额外同步的情况下安全的访问不可变对象,即使在发布这些对象时没有使用同步。
(十五)final类型的域
在没有额外同步的情况下,也可以安全的访问final类型的域,然而如果final类型的域所指向的是可变对象,那么在访问这些域所指向的对象的状态时仍然需要同步。
(十六)如何安全发布一个对象
要安全发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见。一个正确构造的对象可以通过以下方式来发布:
1.在静态初始化函数中初始化一个对象引用
2.将对象的引用保存到volatile类型的域或者AtomicReferance对象中。
3.将对象的引用保存到某个正确构造对象的final类型域中。
4.将对象的引用保存到一个由锁保护的域中 : 如将对象放入到某个容器,vector或synchronizedList。
线程安全库中的容器:Hashtable,synchronizedMap , ConcurrentMap
vector synchronizedList synchronizedSet CopyOnWriteList CopyOnWriteArraySet
BlockingQueue ConcurrentLinkedQueue
类库中的其他数据传递机制(如Future和Exchanger)同样能实现安全发布。
通常,发布一个静态构造的对象,最简单和最安全的方式是使用静态的初始化器:
public static Holder holder = new Holder(42)
(十七)事实不可变对象
如果对象在技术上来看是可变的,但其状态在发布后不会再改变,那么把这种对象称为“事实不可变对象(Effectively Immutable Object)”.
好处:不仅可以简化开发过程,而且还能由于减少了同步而提高性能。
在没有额外的同步情况下,任何线程都可以安全地使用被安全发布的事实不可变对象。
例如,Date本身是可变的,假设需要维护一个Map对象,其中保存了每位用户的最近登录时间:
public Map