sysnchronizedを使用してvolatileを追加していますか?
/**
* 测试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の値を取得する必要があります。揮発性を追加する場合、可視性は保証できますが、原子性は保証できないため、安全ではありません。
安全性を確保するために、メソッドに同期して追加する必要があります。原子性を確保するため
public synchronized void sumCount(){
//方法上加synchronized保证原子性
for (int i = 0; i <1000 ; i++) {
count++; //count++操作在底层是好几步来实现,它本身不是一个原子性的操作
}
}
このようにして得られた値は10000であり、安全です。
sysnchronizedを追加する場合、volatileを追加する必要がありますか?
再確認後、揮発性物質を追加する必要がありますか?
答えは「はい」です。これは通常は検出できませんが、volatileを追加しないと、命令の並べ替えの問題が発生します。instance = new instanse()の場合、このオブジェクトはコンパイラによってコンパイルされ、命令は3つのステップに分割されます
。1。命令のメモリを要求する
2.メンバー変数を初期化する
3.このメモリをインスタンスに割り当てる
命令が3番目のステップを実行する場合まず、このオブジェクトはまだ作成されていませんが、別のスレッドがインスタンスが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();
}
}
}
揮発性で同期化されたものを追加するのは面倒ですが、他の方法はありますか?
はい、cas操作を通じてスレッドセーフを実現できます。
詳細については、ブログの別のcasロックフリー最適化、スピンロック、およびAtomicクラスを参照してください
。