簡介
同步指派就是我們最常用的方式
public class Test {
public static void main(String[] args) {
Test test = new Test();
test.setValue("123");
System.out.println(test.getValue());
}
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
其中test對象中的value set 指派 get取值。這個在單線程中是沒問題的。
但是假如說變成多線程呢。
public class Test {
public static void main(String[] args) {
Test test = new Test();
new Thread(() -> {
//1指派
test.setValue("123");
}).start();
//2擷取值
System.out.println(test.getValue());
}
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
這種情況指派與擷取值,就沒辦法保證先後順序。擷取值的時候有可能另一個線程還沒有指派,或者,已經指派,但線程間的緩存還不一緻。
那線程間的值同步該怎麼做?
方案一 synchronized
package com.hcq.async;
/**
* 同步方法一
*/
public class Model1 {
//使用volatile 防止線程緩存
private volatile String value;
public synchronized String getValue() {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
return value;
}
public synchronized void setValue(String value) {
//先指派,再同步
this.value = value;
this.notify();
}
}
這種方案是最簡單的一步方案。
缺點:
- 鎖比較重
- 多次指派有些問題。
- 如果指派線程在先,會出現死鎖問題。
方案二 synchronized+ volatile
public class Model2 {
//使用volatile 防止線程緩存
private volatile String value;
private volatile boolean isOk = false;
public synchronized String getValue() {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
return value;
}
public synchronized void setValue(String value) {
if (this.isOk == true) {
System.out.println("已經指派[" + this.value + "],指派【" + value + "]失敗!");
} else {
//先指派,再同步
this.isOk = true;
this.value = value;
System.out.println("指派[" + this.value+"]");
this.notify();
}
}
}
這種方案解決了多次指派問題。
- 鎖比較重
- 如果指派線程在先,會出現死鎖問題。
方案三 CountDownLatch + Cas
public class Model3 {
//使用volatile 防止線程緩存
private volatile String value;
private AtomicBoolean isOk = new AtomicBoolean(false);
private CountDownLatch lock = new CountDownLatch(1);
public String getValue() {
try {
lock.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return value;
}
public void setValue(String value) {
//幂等操作
if (isOk.compareAndSet(false, true)) {
//先指派,再同步
this.value = value;
System.out.println("指派[" + this.value+"]");
this.lock.countDown();
} else {
System.out.println("已經指派[" + this.value + "],指派【" + value + "]失敗!");
}
}
}
用AtomicBoolean 來解決重複調用的幂等問題。
用CountDownLatch解決 先指派死鎖問題。
代碼: https://github.com/jlhuang9/iptest/tree/master/src/main/java/com/hcq/async