前言:
Java語言作用很大,因有衆多分門雜類的開源架構導緻Javaer關注高并發細節問題偏少,常常被面試或者面試的時候,别人總是問你current的包,但是卻很少人會詢問你,“這段代碼在高并發的情況下,會出現問題?我們應該如何改寫呢?”是以本篇部落格我想從最簡單的demo,到支援高并發場景的,如果覺得過程中有問題問大家賜教。
案例:
經典多線程并發問題就是生産者消費者問題,以下列子中,我會加入多種條件,而非一個純種的生産者-消費者模式,可能很多人會覺得現在有了concurrent的PKG下的一大堆ArrayBlockQueue,但今天主題是如何優化改進。
demo,非安全的
public class UserAccount {
private int balance;
private long maxMoney = 5000000;
private long minMOney = 1000000;
public UserAccount(int balance) {
this.balance = balance;
}
public void deposit(int amount) {
while (balance + amount < maxMoney) {
balance += amount;
System.out.printf("存:" + balance);
}
}
public void withdraw(int amount) {
while (minMOney > balance - amount) {
balance -= amount;
System.out.println("取:" + balance);
}
}
}
第一步: 上面的代碼很明顯會出現并發問題,簡單點我們加入synchronized
public class UserAccount {
private int balance;
private long maxMoney = 90000000;
private long minMOney = 1000000;
public UserAccount(int balance) {
this.balance = balance;
}
public synchronized void deposit(int amount) {
// synchronized (this) {
while (balance + amount < maxMoney) {
balance += amount;
System.out.println("存:" + balance);
}
}
// }
public synchronized void withdraw(int amount) {
while (minMOney > balance - amount) {
balance -= amount;
System.out.println("取:" + balance);
}
}
}
第二步:有沒有發現加入關鍵字synchronized後,是并發問題解決了,但是我們鎖的範圍太廣了,不懂得可以看下synchronized的說明。(當兩個并發線程通路同一個對象object中的這個 synchronized(this)同步代碼塊時,一個時間内隻能有一個線程得到執行)
public void deposit(int amount) {
synchronized (this) {
while (balance + amount < maxMoney) {
balance += amount;
System.out.println("存:" + balance);
}
}
}
第四步:實際過程中,往往我們不會隻有一個條件,這時候使用到ReentrantLock
private final Lock monitor = new ReentrantLock();
private final Condition low = monitor.newCondition();
private final Condition high = monitor.newCondition();
public UserAccount(int balance) {
this.balance = balance;
}
public void deposit(int amount) {
monitor.lock();
try {
while (balance + amount < maxMoney) {
balance += amount;
System.out.println("存:" + balance);
}
low.signal();
} finally {
monitor.unlock();
}
}
第五步:鎖更新,加入讀寫鎖ReentrantReadWriteLock
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
lock.readLock().lock();
System.out.println(balance);
lock.readLock().unlock();
}
第六步:使用Atomic原子類 第七步:使用violate 假設你的資源類中violate修飾的變量讀的頻繁程度遠遠大于寫,那麼violate修飾的變量就派上用場。
private volatile int value;
public int getValue() {
return value;
}
public int increment() {
synchronized (this) {
return value++;
}
}
第八步 異步處理
CompletableFuture.supplyAsync(() -> {do something();}, taskExecutor);
未完待續...