一、线程安全
1、原子操作。使用java5中的并发库的原子变量可以解决多线程并发访问同一个共享变量。
2、锁
如果对象中有多个共享数据的读写,在多线程环境中,有可能有死锁的情况发生。这时需要用synchronized来控制对共享数据的读写。这里有个概念叫重入(reentrancy)。
可以这么理解:当一个锁被某个线程持有,那么其他线程不可再获取这把锁,但是持有这把锁的线程可以重新持有。其实在运行时环境中有一个对同步锁计数的机制,即锁空闲态时候,count=0,当某个线程持有这把锁后count++,并记录这把锁的owner,当持有这把锁的线程再次持有这把锁后继续count++。
public class Widget { public synchronized void doSomething() { // ... } } public class LoggingWidget extends Widget { public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); //如果没有重入的机制,这里会产生死锁 super.doSomething(); } }
3、用锁保存状态
如何正确使用锁机制,要根据实际情况有一个合适的力度,不能随随便的就把某个方法直接synchronized(可能将不需要同步的部分也加锁,那么对性能造成了一定的影响),也不能将read-modify-write的某一个或者两个操作放入原子操作(没有正确分析原子操作构成)。
对于调用中的多个变量中的每个变量,都必须要持有同一个锁。
二、共享对象
1、可见性
In the absence of synchronization, the compiler, processor, and runtime
can do some downright weird things to the order in which operations ap-
pear to execute. Attempts to reason about the order in which memory
actions “must” happen in insufficiently synchronized multithreaded pro-
grams will almost certainly be incorrect.
简单的来说,就是多线程的环境下,情况是多变而复杂的,所以,对于需要读写的变量,最好的方法就是synchronized 。
锁与可见性
书中的图很好的描述了该问题
Volatile Variables 可变变量:程度较轻的 synchronized”
非volatile变量在多线程运行环境中, 在每个线程中有一块临时内存存放,也就去他们的访问的事各自临时内存块的数据,而volatile变量则是放在一个公共的环境共多线程访问,即达到了可见性。
2、Publication and escape 公开与逃逸
在JAVA中,通过public,protect,private等方法可以控制对象的访问权限,但是如果你处理不当,可能会使有的不应该被访问的数据被处理。
class UnsafeStates { private String[] states = new String[] { "AK", "AL" // ... }; public String[] getStates() { return states; } }
返回的states变量是一个数组地址,调用方法是可以修改该数组的值。
3、Thread Confinement线程限制
堆限制:局部变量是线程相关的,所以只要改局部变量没有逃逸,就保证是线程安全的。
ThreadLocal:每个线程都保存在一个键值对中,取出的时候只取出和本线程相关的值。典型的用空间换时间,比较常见在连接池中使用。
不变:不变的对象是线程安全的,这种对象是创建完了以后,只读。一般是用final修饰。
4、安全发布