《JAVA核心技术卷·I》——第十二章7——同步3——竞态条件的进阶——条件对象——《并发》

🌈hello,你好鸭,我是Ethan,西安电子科技大学大三在读,很高兴你能来阅读。

✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。
🏃人生之义,在于追求,不在成败,勤通大道。加油呀!

🔥个人主页:Ethan Yankang
🔥推荐:史上最强八股文||一分钟看完我的几百篇博客

🔥温馨提示:划到文末发现专栏彩蛋   点击这里直接传送

🔥本篇概览:详细讲解了条件对象的使用已改进程序的功能。🌈⭕🔥


【计算机领域一切迷惑的源头都是基本概念的模糊,算法除外】


目录

🌈序言:

🌈引出:

WHAT

🌈条件变量

🌈引出

🌈signalAll()注意事项

🌈signalAll()的使用

🌈signalAll()与signal()

🌈警告 

🌈API

🌈完整源码


🌈序言:

JAVA基础必序扎实的一批,此关不过,啥都没有。今日得《JAVA核心技术·卷I》之良品辅助,应按本书学之习之,时时复习,长此以往必能穿魂入脉,习得大功。

感谢Cay S. Horstmann给世界留下如此优美的作品。

对于一个强烈想完全掌握JAVA的技术宅来说,JAVA的XXX万万不能放过,这些基础的概念例程都值得细细体味的,千万别觉得都是文字,浪费时间,记住——别违背科学发展的客观规律。别一味地赶进度以满足自己学的都么快的虚荣心,自欺欺人,要老老实实的走好每一步。

上一篇文章详细讲解了使用ReentrantLock解决竞态条件的问题,建议先将这部分知识掌握之后再来学习本篇内容,点击查看。


🔥《JAVA核心技术卷·I》——第十二章6——同步2——竞态条件的解决方法之一——ReentrantLock——《并发》-CSDN博客

🔥 所有JAVA基础一键查阅(含习题集)-CSDN博客


🌈引出


WHAT


🌈条件变量

通常,线程进入临界区后却发现只有满足了某个条件之后它才能执行。可以使用一个条件对象来管理那些已经获得了一个锁却不能做有用工作的线程。在这一节里,我们会介绍Java库中条件对象的实现(由于历史原因,条件对象经常被称为条件变量(conditionalvariable))。


🌈引出

现在来优化银行的模拟程序。如果一个账户没有足够的资金转账,我们不希望从这样的账户转出资金。注意不能使用类似下面的代码:

if(bank.getBalance(from)>= amount)
    bank.transfer(from,to, amount);

在成功地通过这个测试之后,但在调用transfer方法之前,当前线程完全有可能被中断。

if(bank.getBalance(from)>= amount)
// thread might be deactivated at this point
    bank.transfer(from,to,amount);


在线程再次运行前,账户余额可能已经低于提款金额。必须确保在检查余额与转账活动之间没有其他线程修改余额。为此,可以使用一个锁来保护这个测试和转账操作:

public void transfer(int from, int to, int amount){

    bankLock.lock();
        try{

        while(accounts[from]< amount)
                // wait;   
一直阻塞直至满足条件。但又有一个问题,他还没释放锁,别人又不能给他转钱,余额就永远改变不了
此时就是条件变量的用武之地了

         // transfer funds

        }finally{

     bank.Lock.unlock();
    }

}

一个锁对象可以有一个或多个相关联的条件对象。你可以用newCondition方法获得一个条件对象。习惯上会给每个条件对象一个合适的名字来反映它表示的条件。例如,在这里我们建立了一个条件对象来表示“资金充足”条件。


如果 transfer方法发现资金不足,它会调用

sufficientFunds.await();

当前线程现在暂停,并放弃锁。这就允许另一个线程执行,我们希望它能增加账户余额。等待获得锁的线程和已经调用了await方法的线程存在本质上的不同。

一旦一个线程调用了await方法,它就进人这个条件的等待集(wait set)。当锁可用时,该线程并不会变为可运行状态。实际上,它仍保持非活动状态,直到另一个线程在同一条件上调用signalAll方法。
当另一个线程完成转账时,它应该调用

sufficientFunds.signalAll();

这个调用会重新激活等待这个条件的所有线程。当这些线程从等待集中移出时,它们再次成为可运行的线程,调度器最终将再次将它们激活。同时,它们会尝试重新进入该对象。一但锁可用,它们中的某个线程将从await调用返回,得到这个锁,并从之前暂停的地方继续执行。


此时,线程应当再次测试条件。不能保证现在一定满足条件一signalAll方法仅仅是通
知等待的线程:现在有可能满足条件,值得再次检查条件。


🌈signalAll()注意事项

最终需要有某个其他线程调用signalAll方法,这一点至关重要。当一个线程调用await()时,它没有办法重新自行激活。它寄希望于其他线程。如果没有其他线程来重新激活等待的线程,它就永远不再运行了。这将导致令人不快的死锁(deadlock)现象。如果所有其他线程都被阻塞,最后一个活动线程调用了await方法但没有先解除另外某个线程的阳塞,现在这个线程也会阻塞。此时没有线程可以解除其他线程的阻塞状态,程序会永远挂起。


🌈signalAll()的使用

其他账号完成转账后就调用,以唤醒对应的条件的线程再次匹配测试之。


🌈signalAll()与signal()

注意signalAll调用不会立即激活一个等待的线程。它只是解除等待线程的阻塞,使这些线程可以在当前线程释放锁之后竞争访问对象。
另一个方法 signal只是随机选择等待集中的一个线程,并解除这个线程的阻塞状态。这比解除所有线程的阻塞更高效,但也存在危险。如果随机选择的线程发现自己仍然不能运行,它就会再次阻塞。如果没有其他线程再次调用signal,系统就会进入死锁。


🌈警告 


🌈API


🌈完整源码

import java.util.Arrays;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Bank2 {

        private final double[] accounts;
        private Lock bankLock;
        private Condition sufficientFunds;

        Bank2(int n, double initialBalance) {
            this.accounts = new double[n];
            Arrays.fill(accounts, initialBalance);
                    创建可冲入锁
            bankLock = new ReentrantLock();
            sufficientFunds = bankLock.newCondition();
        }

    public void transfer(int from, int to, double amount) throws InterruptedException {
         bankLock.lock();
            try {
                if (accounts[from] < amount)
                    不满足就等待,线程让出锁让别的线程来给你转账,让你满足后再执行
                    sufficientFunds.await();
                System.out.print("线程信息:" + Thread.currentThread());
                System.out.print("\t线程I D:" + Thread.currentThread().getId());
                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 account : accounts) {
                    sum += account;
                }
                return sum;
            } finally {
                bankLock.unlock();
            }
        }
        
        private int size() {
            return accounts.length;
        }
}

 



💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖

热门专栏推荐

🌈🌈计算机科学入门系列                     关注走一波💕💕

🌈🌈CSAPP深入理解计算机原理        关注走一波💕💕

🌈🌈微服务项目之黑马头条                 关注走一波💕💕

🌈🌈redis深度项目之黑马点评            关注走一波💕💕

🌈🌈JAVA面试八股文系列专栏           关注走一波💕💕

🌈🌈JAVA基础试题集精讲                  关注走一波💕💕   

🌈🌈代码随想录精讲200题                  关注走一波💕💕


总栏

🌈🌈JAVA基础要夯牢                         关注走一波💕💕  

🌈🌈​​​​​​JAVA后端技术栈                          关注走一波💕💕  

🌈🌈JAVA面试八股文​​​​​​                          关注走一波💕💕  

🌈🌈JAVA项目(含源码深度剖析)    关注走一波💕💕  

🌈🌈计算机四件套                               关注走一波💕💕  

🌈🌈数据结构与算法                           ​关注走一波💕💕  

🌈🌈必知必会工具集                           关注走一波💕💕

🌈🌈书籍网课笔记汇总                       关注走一波💕💕         



📣非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤收藏✅ 评论💬,大佬三连必回哦!thanks!!!
📚愿大家都能学有所得,功不唐捐!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值