无状态对象(既不包含任何域,也不包含任何其他类中域的引用)一定是线程安全的。
Final
fianl域是不能修改的(但如果final域所引用的对象是可变的,那么这些被引用的对象是可变的)
除非需要可变域,否则应将其声明位final域。
初始化:
final int i = 0; //在声明时就初始常量的值(每个对象的fianl常量都为一个值)
public class Test{ //声明时未初始化,如果常量不通过构造方法或块初始化会报错
final int i;
{
i=1; //不可以再在构造函数里初始化i的值,因为块已经把常量初始化了(每个对象的fianl常量都为一个值)
}
Test(){
i=1; //通过构造方法初始化(如果有参构造器,每个对象的fianl常量可以是不同值)
}
}
public class Test{ //静态类中初始化(每个对象的fianl常量都为一个值)
static final int i;
static{
i=1;
}
}
可重入锁:为了避免死锁
当一个线程获取对象锁,需要再次获取这个对象锁:
1. 一个对象中有多个同步方法
2. 子类重写父类同步方法:
super不是指父类对象,而是一个引用,引用从父类继承来的方法。
super.父类方法,通过super引用调用从父类继承来的方法,锁对象仍然是子类对象。
public class aaa{
public static void main(String[] args) {
ccc ccc = new ccc();
ccc.dothing();
}
}
class bbb{
public void dothing(){
System.out.println("父类:"+this.toString());
}
}
class ccc extends bbb{
@Override
public void dothing() {
super.dothing();
System.out.println("super:"+super.toString());
System.out.println("子类:"+this.toString());
}
}
输出:
父类:ccc@62043840
super:ccc@62043840
子类:ccc@62043840
安全发布
不安全发布(多线程问题):
public Holder holder;
public void init() {
holder = new Holder(42);
}
由于可见性问题,其他线程看到的Holder对象可能处于不一致状态。
安全发布(保证可见性):
静态初始化器中初始化对象引用(public static Holder holder = new Holder(24);):最简单,最安全
将对象引用保存在volatile域或者AtomicReferance对象中
对象引用保存在final域中
对象引用保存在由锁保护的域中
安全发布容器:
ConcurrentMap
CopyOnWriteArrayList
BlockingQueue
可变对象:
不仅需要安全发布,并且必须是线程安全的。
解决:
线程封闭:ThreadLocal
只读共享:CopyOnWriteArrayList 和 CopyOnWriteArraySet
同步:synchronizer
保护对象:AtomicReferance