加volatile还用加sysnchronized吗
/**
* 测试volatile不具有原子性
*/
public class T {
volatile int count=0; //属性上加volatile 保证可见性
public void sumCount(){
for (int i = 0; i <1000 ; i++) {
count++; //count++操作在底层是好几步来实现,它本身不是一个原子性的操作
}
}
public static void main(String[] args) {
T t=new T();
List<Thread> threads=new ArrayList<>();
for (int i = 0; i <10 ; i++) {
threads.add(new Thread(() ->{
t.sumCount();
}));
}
threads.forEach((o)->{
o.start();
});
threads.forEach((o)->{
try {
o.join(); //保证main线程到当前线程执行完成后再执行
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.count);
//理论上应该得到的值是10000,在加volatile的情况下,虽然它能保证可见性,但是不能保证原子性,
//所以的到的结果不对,在方法上加synchronized来保证原子性就安全了
}
}
理论上应该得到的值是10000,在加volatile的情况下,虽然它能保证可见性,但是不能保证原子性,所以也是不安全的
为了保证安全,需要在方法上加synchronized保证原子性
public synchronized void sumCount(){
//方法上加synchronized保证原子性
for (int i = 0; i <1000 ; i++) {
count++; //count++操作在底层是好几步来实现,它本身不是一个原子性的操作
}
}
这样得到的值是10000,保证安全
加sysnchronized还用加volatile吗
双重检查完了,要不要加volatile呢?
答案是肯定的,虽然这个一般测不出来,但是不加volatile会出现指令重排序的问题!instance=new instanse()这个对象在编译器编译的时候指令分为三步:
1.给指令申请内存
2.给成员变量初始化
3.把这块内存赋值给instance
假如指令先执行了第三步,那么这个对象正在还没有创建出来,但是另一个线程过来一看,instance不是null,就不会走加锁的那段代码,直接来用这个对象,可是这个对象是个半初始化的对象,这样的话就会出问题了
/**
* 懒汉模式4 -双重加锁
*
*/
public class Singleton05 {
private volatile static Singleton05 singleton05; //需要加VOLATILE禁止指令重排序
private Singleton05() {
}
public static Singleton05 getInstance() throws InterruptedException {
if (singleton05 == null) {
//加双重判断
synchronized (Singleton05.class){
if(singleton05==null){
Thread.sleep(1);
singleton05 = new Singleton05();
}
}
}
return singleton05;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
try {
System.out.println(Singleton05.getInstance().hashCode());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
加volatile还加sysnchronized好麻烦,能不能有别的办法呢?
有的,我们可以通过cas操作来实现线程安全
详见我博客另一篇
cas无锁优化,自旋锁 ,Atomic类