保证线程安全的方法:
限制可变变量的共享:
局部变量保存在线程栈中,每个调用都有自己的变量副本, 局部变量如果是对象的引用,则要确保不能引用任何其他线程可访问的对象(针对可变对象)。
避免全局变量的使用:全局静态变量不会自动受到线程访问限制, 如果使用了全局静态变量,则应说明只允许一个线程使用它们,所以在多线程中应取消全局变量。
用不可变的共享变量:
不可变解决了因为共享可变数据造成的竞争,并简单地通过使用共享数据不可变来解决它。
一种常见的方式是使用final: final变量不允许再赋值,所以声明为final的变量可以安全地从多个线程访问。因为这种安全性只适用于变量本身,仍然必须确保变量指向的对象是不可变的。
将共享数据封装在线程安全的数据类型中:
将共享的可变数据存储在线程安全的数据类型中。因为线程安全的数据类型性能较差,所以Java对一些可变数据类型提供两种形式供选择:线程安全的和线程不安全的。
例:StringBuffer 和 StringBuilder都是可变数据类型,功能基本相同
StringBuffer是线程安全的,StringBuilder不是
StringBuilder性能更好,推荐在单线程程序中使用
Java提供的线程安全的Collections类型版本确保了方法是原子的,也就是动作的内部操作不会同其他操作交叉,不会产生部分完成的情况。
使用同步机制来防止线程同时使用变量:
防止多个线程同时访问共享数据,所以并发模块彼此间采用同步的方式共享内存,以解决竞争带来的问题。
常用的方法是使用锁机制:锁是一种抽象,某时刻最多只允许一个线程拥有锁。
锁的获取:如果锁被其他线程拥有,将进入阻塞状态,等待锁释放后,再同其他线程竞争获取锁的拥有权。
锁机制可以确保锁的拥有者始终查看最新的数据,避免了reordering问题。
同步语句的锁: 同步区域提供了互斥功能: 一次只能有一个线程处于由给定对象的锁保护的同步区域中。如:
锁只能确保与其他请求获取相同对象锁的线程互斥访问,如果其他线程没有使用synchronized(obj)或者利用了不同的锁,则同步会失效,需要仔细检查和设计同步块和同步方法。
同步方法的锁:
当线程调用同步方法时,它会自动获取该方法所在对象的内部锁,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会释放锁。
当一个线程在执行一个对象的同步方法时,所有其他线程如果调用同一对象的同步方法块,则会挂起执行,直到第一个线程针对此对象的操作完成。
实现的两种方法如下:
注:同步方法不用指定锁,但同步语句/块必须指定锁。
在涉及到线程安全之后,要在ADT的spec中说明是不是线程安全的。