先来看Bank.java的代码:
package com.example.lib.threads;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;
public class Bank {
private ReentrantLock bankLock = new ReentrantLock();
private final double[] accounts;
public Bank(int n,double initialBalance) {
accounts = new double[n];
Arrays.fill(accounts,initialBalance);
}
public void transfer(int from,int to,double amount) {
//bankLock.lock();
//if (accounts[from] < amount) return;
try {
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 banlance : %10.2f%n", getTotalBanlance());
}
finally {
//bankLock.unlock();
}
}
public double getTotalBanlance() {
double sum = 0;
for (double a : accounts) {
sum += a;
}
return sum;
}
public int size() {
return accounts.length;
}
}
在上述代码中,没有加锁去保护线程的访问。
在以下输出中:
package com.example.lib.threads;
import sun.rmi.runtime.Log;
public class BankTest {
public static final int NACCOUNTS = 100;
public static final double INITIAL_BANLANCE = 1000;
public static final int MAX_AMOUNT = 1000;
public static final int DELAY =10;
public static void main(String[] args) {
Bank bank = new Bank(NACCOUNTS,INITIAL_BANLANCE);
for (int i = 0;i < MAX_AMOUNT;i++) {
int fromAccount = i;
Runnable r = () -> {
try {
while (true) {
int toAccount = (int) (bank.size() * Math.random());
System.out.println("toAccount = " + toAccount);
double amount = MAX_AMOUNT * Math.random();
System.out.println("amount = " + amount);
//Log.getLog("w",String.valueOf(amount));
bank.transfer(fromAccount,toAccount,amount);
Thread.sleep((int) (DELAY * Math.random()));
}
} catch (InterruptedException e) {
}
};
Thread thread = new Thread(r);
thread.start();
}
}
}
输出结果:
772.04 from 66 to 99; Total banlance : 99014.39
toAccount = 73
amount = 350.2780310321255
Thread[Thread-80,5,main]
350.28 from 80 to 73; Total banlance : 99014.39
985.61 from 0 to 97; Total banlance : 100000.00
toAccount = 0
toAccount = 46
toAccount = 3
amount = 139.6185107884077
Thread[Thread-5,5,main]
139.62 from 5 to 3; Total banlance : 100000.00
toAccount = 28
可以看到,banlance有时是错的。
所以需要加锁:
package com.example.lib.threads;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;
public class Bank {
private ReentrantLock bankLock = new ReentrantLock();
private final double[] accounts;
public Bank(int n,double initialBalance) {
accounts = new double[n];
Arrays.fill(accounts,initialBalance);
}
public void transfer(int from,int to,double amount) {
bankLock.lock();
//if (accounts[from] < amount) return;
try {
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 banlance : %10.2f%n", getTotalBanlance());
}
finally {
bankLock.unlock();
}
}
public double getTotalBanlance() {
double sum = 0;
for (double a : accounts) {
sum += a;
}
return sum;
}
public int size() {
return accounts.length;
}
}
加锁的方法也很简单:
private ReentrantLock bankLock = new ReentrantLock();//new一个锁
然后在需要保护的操作前注明:bankLock.lock();
等相关操作完成后,再 bankLock.unlock();即可。
需要主要的是:
lock操作需要在try中使用;而unlock操作需要在finally中完成。