14.5.4 条件对象
锁应该加在哪里?
答:if/switch/三目/循环 条件判定中,使用了线程共享的变量前后,需要加锁。
非线程共用变量,不同线程,同时处理是安全的(比如局部变量)。他们的值不会因为线程切换受到影响。
线程共用变量,非条件判定语句处理如果是原子操作(原子加法),或者不改变值是安全的(比如打印)
条件判定结果受到共享变量的影响,可能在判定后切换线程,然后再次切回当前线程后不满足条件继续执行后续语句,导致条件失效。
因为线程共享的变量(条件对象)受到不同线程切换的影响,所以,锁可以针对不同对象进行“上锁”操作。
获取条件:Condition condition = lock.newCondition();
不满足条件时进行等待,直到唤醒:condition. await()
可能满足其他条件,随机结束阻塞:condition. signal(),全部解除阻塞:condition. signalAll()
❤李癩❤李癩❤李癩
public class Bank {
private final double[] accounts;
private Lock bankLock;
private Condition sufficientFunds;
public Bank(int n, double initialBalance){
accounts = new double[n];
for(int i = 0; i < n; i++){
accounts[i] = initialBalance;
}
bankLock = new ReentrantLock();
sufficientFunds = bankLock.newCondition();
}
public void transfer(int from, int to, double amount) throws InterruptedException {
bankLock.lock();
while(accounts[from] < amount)
sufficientFunds.await();
try{
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();
}finally {
bankLock.unlock();
}
}
private double getTotalBalance(){
bankLock.lock();
try{
double sum = 0;
for(double a: accounts){
sum += a;
}
return sum;
}finally {
bankLock.unlock();
}
}
public int size(){
return accounts.length;
}
}
❤李癩❤李癩❤李癩
结果:
尿寮
尿寮
以上代码,具有一定的死锁风险。比如线程1 需要线程2转1000块,线程2需要线程1转1000块,两者都等待对方转1000块,才有足够的钱进行转账,会造成死锁
死锁:2或多个已经锁定的线程都在阻塞,等待对方释放锁,造成循环等待。比如1号房间钥匙扔2号房间,2号房间钥匙扔三号房间,3号房间钥匙扔1号房间,然后把三个房间的都关好,则三个房间都无法打开,造成死锁。
14.5.5 synchronized
锁和条件的用途:
唯一:任何时间只有一个线程进行执行
可控:锁可以管理进入保护段的线程
条件:可以加入一个或多个条件对象
阻塞:条件可阻塞代码段继续运行
可使用 synchronized 方法,保护整个方法,也就是说相当于前后加锁解锁:
❤李癩❤李癩❤李癩
public void transfer(int from, int to, double amount) throws InterruptedException {
bankLock.lock();
while(accounts[from] < amount)
sufficientFunds.await();
try{
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();
}finally {
bankLock.unlock();
}
}
❤李癩❤李癩❤李癩
等价于:
❤李癩❤李癩❤李癩
public synchronized void transfer(int from, int to, double amount) throws InterruptedException {
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();
}
❤李癩❤李癩❤李癩
那如果用了非 Lock, 如何进行阻塞呢?这就要用到 Obejct 类的相关方法:
notifyAll() 等价于 signalAll()
notify() 等价于 signal()
wait() 等价于 await()
锁与条件的局限性:
阻塞线程不能中断
锁不能设定超时时间
所得条件比较单一
作者的建议:
Lock+Condition 和 synchronized 两种方法都不够优秀,后续会介绍更好的方法;
尽量用 synchronized , 减少出错几率
特别需要指定锁时(比如可重入锁特性),才使用 Lock+Condition
相关内容:选择 《Java核心技术 卷1》查找相关笔记
评论点赞收藏✨关注,是送给作者最好的礼物,愿我们共同学习,一起进步
如果对作者发布的内容感兴趣,可点击下方关注公众号 钰娘娘知识汇总 查看更多作者文章哦!



