前言:
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);
未完待续...