您的位置:首页 > 编程语言 > Java开发

线程通信

2016-05-31 14:16 375 查看

1.可见性

1.1.非原子的64位操作

没有声明为volatile的64位数值变量(double、long),JVM允许将64位的读或写划分为俩个32位操作。如果读写在不同的线程,可能会出现得到一个值的高32位和另一个值的低32位。当JVM的规范完成是,很多主流的处理器框架还不能有效地支持64为算数原子操作。

1.2.加锁

锁不仅仅是关于同步与互斥的,也是关于内存可见的。为了保证所有线程都能够看到共享的、可变的最新之,读取与写入线程必须使用公共的锁进行同步。

1.3.Volatile变量

volatile变量不会缓存在寄存器或缓存在对其他处理器隐藏的地方,读一个volatile类型的变量时,总会返回由某一线程所写入的最新值。

volatile变量对于自增自减存在不足,不能做到原子操作(读-改-写),加锁或原子类进行代替。加锁可以保证可见性与原子性,volatile变量只能保证可见性。

2.线程封闭

访问共享、可变的数据,都是要求同步,避免过多的同步的方式可以是不共享数据,该数据只能被唯一的线程访问,就不需要任何同步。线程封闭是最简单直接的实现线程安全的方式之一。

2.1.Ad-hoc线程限制

Ad-hoc是"非正式的",如餐厅中,你决定与某人谈谈业务,这就是一种"Ad-hoc"会谈。Ad-hoc是只未经过设计而得到的线程封闭行为。因为没有可见性修饰符与本地变量等语言特性协助将对象限制在目标线程上,所以这种方式是非常容易出错的。

2.2.栈限制

栈限制是线程限制的一种特例,只能通过本地变量才能触及对象。

public int calculate(Collection<Item> items){
SortSet<Item> items0;
int c=0;

// items0 被限制在方法中,不要让其逸出
items0 = new TreeSet<Item>(new ItemComparator());
items0.addAll(items);
for(Item i: items0){
if(i.isFit())
c++;
}
return c;
}


2.3.ThreadLocal

ThreadLocal允许你将每个线程与持有数值的对象关联在一起。ThreadLocal提供了get与set访问器,为每个使用它的线程维护一份单独的拷贝,所以get总是返回由当前执行线程通过set设置的最新值。

private ThreadLocal<Connection> local = new ThreadLocal<Connection>(){
public Connection initialValue(){
try {
return DriverManager.getConnection(DB_URL);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
};
public void getC() {
local.get();
}
#1:线程首次调用ThreadLocal.get方法时,会请求initialValue提供一个初始值。

#2:概念上可以把ThreadLocal<T>看作Map<Thread , T>,存储了与线程相关的值,不过事实上实现并非如此。当线程终止时,对应的值会被垃圾回收。

3.不可变性

不可变对象永远是线程安全的。

只要满足如下状态,一个对象才是不可变的:

#1:他的状态不能在创建后再被修改

#2:所有域都是final类型

#3:它被正确创建(创建期间没有发生this引用逸出)

4.发布对象

发布对象的必要条件依赖于对象的可见性

#1:不可变对象可以通过任意机制发布;

#2:高效不可变对象必须安全发布,如java.util.concurrent包内工具类;

#3:可变对象必须要安全发布,同时必须线程安全或被锁守护。

并发中,有效共享对象的策略:

#1:线程限制:线程独占且只能被占有它的线程修改。

#2:共享只读:多条线程并发访问,任何线程都不能修改它。共享只读对象包括高效不可变对象、可变对象。

#3:共享线程安全:对象内部进行同步,所有其他线程无需额外同步,就可以通过公共接口随意地访问它。

#4:被守护:通过特定的锁来访问。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息