Java多线程(五) ReentrantLock、Lock和Condition的用法
ReentrantLock类:
- public ReentrantLock(boolean fair) {
- sync = fair ? new FairSync() : new NonfairSync();
- }
实例如下:
Bank类如下:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Bank {
// private ReentrantLock myLock = new ReentrantLock();// 互斥锁 Lock
// Lock 实现提供了比使用 synchronized
// 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
private Lock myLock;
// Condition: 条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true
// 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式
// 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。
private Condition sufficientFunds;
private final double[] accounts;
public Bank(int n, double initialBalance) {
accounts = new double[n];
for (int i = 0; i < accounts.length; i++)
accounts[i] = initialBalance;
// ReentrantLock()构建一个可以被用来保护临界区的可重入锁
// ReentrantLock(boolean fair)
// 构建一个带有公平策略的锁。一个公平锁偏爱等待时间最长的线程。但是这个公平的保证将大大降低性能。所以,默认情况下,锁没有强制为公平的
myLock = new ReentrantLock();// 创建互斥锁
sufficientFunds = myLock.newCondition();// newCondition() 返回绑定到此 Lock
// 实例的新 Condition 实例。
// 返回一个与该锁相关的条件对象
}
public void transfer(int from, int to, double amount) {
myLock.lock();// lock()获取这个锁,如果锁同时被另一个线程拥有则发生阻塞
try {
while (accounts[from] < amount)
sufficientFunds.await();// 将该线程放到条件的等待集中
System.out.print(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();// 解除该条件的等待集中的所有线程的阻塞状态
// sufficientFunds.signal();//从该条件的等待集中随机第选择一个线程,解除其阻塞状态
} catch (Exception e) {
myLock.unlock();// unlock()释放这个锁
}
}
public double getTotalBalance() {
myLock.lock();
double sum = 0;
try {
for (double a : accounts)
sum += a;
} catch (Exception e) {
myLock.unlock();
}
return sum;
}
public int size() {
return accounts.length;
}
}
TransferRunnable类如下:
public class TransferRunnable implements Runnable {
private int fromAccount;
private double maxAmount;
private int DELAY = 10;
private Bank bank;
public TransferRunnable(Bank bank, int fromAccount, double maxAmount) {
this.bank = bank;
this.fromAccount = fromAccount;
this.maxAmount = maxAmount;
}
public void run() {
try {
while (true) {
int toAccount = (int) (bank.size() * Math.random());
double amount = maxAmount * Math.random();
bank.transfer(fromAccount, toAccount, amount);
Thread.sleep((int) (DELAY * Math.random()));
}
} catch (InterruptedException e) {
}
}
}
测试类UnsynchBankTest 如下:
public class UnsynchBankTest {
public static final int NACCOUNTS = 100;
public static final double INITIAL_BALANCE = 1000;
public static void main(String[] args) {
Bank b = new Bank(NACCOUNTS, INITIAL_BALANCE);
int i;
for (i = 0; i < NACCOUNTS; i++) {
TransferRunnable r = new TransferRunnable(b, i, INITIAL_BALANCE);
Thread t = new Thread(r);
t.start();
}
}
}
运行结果如下:
Thread[Thread-1,5,main] 0.01 from 1 to 34 Total Balance: 100000.00
Thread[Thread-1,5,main] 19.60 from 1 to 98 Total Balance: 100000.00
Thread[Thread-1,5,main] 879.09 from 1 to 92 Total Balance: 100000.00
Thread[Thread-0,5,main] 840.53 from 0 to 25 Total Balance: 100000.00
Thread[Thread-4,5,main] 870.95 from 4 to 82 Total Balance: 100000.00
Thread[Thread-2,5,main] 658.99 from 2 to 17 Total Balance: 100000.00
Thread[Thread-3,5,main] 449.57 from 3 to 4 Total Balance: 100000.00
Thread[Thread-3,5,main] 123.89 from 3 to 84 Total Balance: 100000.00
Thread[Thread-3,5,main] 38.94 from 3 to 80 Total Balance: 100000.00
下面对这些知识点做个总结:
使用Lock和Condition对象,记住他们的关键之处:
1、锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码。
2、锁可以管理试图进入被保护代码段的线程。
3、锁可以拥有一个或多个相关的条件对象。
4、每个条件对象管理那些已经进入被保护的代码段但还不能运行的线程。