本文记录Java多线程同步的案例。
背景:银行(Bank)里面有多个账户,将两个账户进行转账(调用Bank对象的transfer方法),这在个过程中,银行里钱的总额保持不变。
客户端:开启多个线程进行转账操作
import java.util.Random;
/**
* UnsynchBankTest
*/
public class UnsynchBankTest {
public static final int NACCOUNTS = 100;
public static final double INITIAL_BALANCE = 1000;
public static final double MAX_AMOUNT = 1000;
public static final int DELAY = 10;
public static void main(String[] args) {
//Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
// BankWithConditionClock bank = new BankWithConditionClock(NACCOUNTS, INITIAL_BALANCE);
BankInLockTest bank = new BankInLockTest(NACCOUNTS, INITIAL_BALANCE);
for (int i=0; i< NACCOUNTS; i++){
int fromAccount = i;
Runnable r = () ->{
try{
while(true){
int toAccount = (int)(bank.size() * Math.random());
double amount = MAX_AMOUNT * Math.random();
bank.transfer(fromAccount, toAccount, amount);
Thread.sleep((int)(DELAY * Math.random()));
}
}catch(InterruptedException e){
e.printStackTrace();
}
};
Thread t = new Thread(r);
t.start();
}
}
}
方案一:使用使用锁的方案进行同步
import java.util.Arrays;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Bank
*/
public class Bank {
/**
* 多个银行账户
*/
private final double[] accounts;
// 使每一个对象有自己的ReentrantLock对象
private Lock bankLock = new ReentrantLock();
public Bank(int n, double initialBalance){
accounts = new double[n];
Arrays.fill(accounts, initialBalance);
}
/**
* 银行交易
* @param from
* @param to
* @param amount
*/
public void transfer(int from, int to, double amount){
bankLock.lock();
try{
if(accounts[from]<amount) return;
System.out.println(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f from %d to %d", amount, from , to);
accounts[to] += amount;
System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
}finally{
bankLock.unlock();
}
}
/**
* 获取银行总钱数
* @return
*/
public double getTotalBalance(){
double sum = 0;
for (double a : accounts) {
sum += a;
}
return sum;
}
public int size(){
return accounts.length;
}
}
方案二:使用锁对象和条件对象
当不满足转账条件的时候,将线程等待,直到满足条件再执行。
import java.util.Arrays;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* BankWithConditionClock
*/
public class BankWithConditionClock {
/**
* 多个银行账户
*/
private final double[] accounts;
// 使每一个对象有自己的ReentrantLock对象
private Lock bankLock;
private Condition sufficientFunds;
public BankWithConditionClock(int n, double initialBalance){
accounts = new double[n];
Arrays.fill(accounts, initialBalance);
bankLock = new ReentrantLock();
sufficientFunds = bankLock.newCondition();
}
/**
* 银行交易
* @param from
* @param to
* @param amount
* @throws InterruptedException
*/
public void transfer(int from, int to, double amount) throws InterruptedException{
bankLock.lock();
try{
// 加条件锁
while(accounts[from]<amount) {
sufficientFunds.await();
}
System.out.println(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f from %d to %d", amount, from , to);
accounts[to] += amount;
System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
// 唤醒处于等待的其他所有线程
sufficientFunds.signalAll();
}finally{
bankLock.unlock();
}
}
/**
* 获取银行总钱数
* @return
*/
public double getTotalBalance(){
double sum = 0;
for (double a : accounts) {
sum += a;
}
return sum;
}
public int size(){
return accounts.length;
}
}
方案三:使用synchronized
import java.util.Arrays;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* BankInLockTest
*/
public class BankInLockTest {
/**
* 多个银行账户
*/
private final double[] accounts;
public BankInLockTest(int n, double initialBalance) {
accounts = new double[n];
Arrays.fill(accounts, initialBalance);
}
/**
* 银行交易
*
* @param from
* @param to
* @param amount
* @throws InterruptedException
*/
public synchronized void transfer(int from, int to, double amount) throws InterruptedException{
// 加条件锁
while (accounts[from] < amount) {
wait();
}
System.out.println(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f from %d to %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
notifyAll();
}
/**
* 获取银行总钱数
*
* @return
*/
public double getTotalBalance() {
double sum = 0;
for (double a : accounts) {
sum += a;
}
return sum;
}
public int size() {
return accounts.length;
}
}