zoukankan      html  css  js  c++  java
  • 【高并发】学好并发编程,关键是要理解这三个核心问题

    写在前面

    写【高并发专题】有一段时间了,一些读者朋友留言说,并发编程很难,学习了很多的知识,但是在实际工作中却无从下手。对于一个线上产生的并发问题,又不知产生这个问题的原因究竟是什么。对于并发编程,感觉上似乎是掌握了,但是真正用起来却不是那么回事!

    其实,造成这种现象的本质原因就是没有透彻的理解并发编程的精髓,而学好并发编程的关键是需要弄懂三个核心问题:分工、同步和互斥

    分工

    比较官方的解释为:分工就是将一个比较大的任务,拆分成多个大小合适的任务,交给合适的线程去完成,强调的是性能。

    如果你还不能够理解什么是分工,这里,我们可以做一个假设。假设你是一个XXX上市公司的CEO,你的工作是如何管理好你的公司。但是,就如何管理好公司而言,涉及到的任务就比较多了,我们可以将其看做一个很大的任务,这个很大的任务,细看的话可以包括:人员招聘和管理、产品设计和开发、运营和推广、公司税务等等。那细化后这么多的任务交给你一个人去做,想必你一定是崩溃的。即使你能够挺住,估计你一个人把这所有的任务完成,那黄花菜也就凉了!到时,估计你就会偷偷的躲在角落里唱“凉凉”了。。。

    所以,如果你真的想管理好你的公司,你就需要将这些任务分解,分工细化,将人员招聘和管理的任务交给人力资源部门去完成,将产品的设计交给设计部门去完成,将产品的开发交给开发部门去完成,将运营和推广交给运营和市场部门去完成,将公司税务交给财务部门去完成。此时,你的任务就是及时了解各个部门的工作情况,统筹并协调各部门的工作,并思考如何规划公司的未来。

    其实,这里你将管理公司的任务拆解、细化分工之后,你会发现,其实各部门之间的工作是并行执行的。比如:人力资源部门在管理员工的绩效考核时,同时产品设计和开发部门正在设计和开发公司的产品,与此同时,公司的运营正在和设计与开发沟通如何更好的完善公司的产品,而推广部门正在加大力度宣传和推广公司的产品。而财务部门正在统计和计算公司的各种财务报表等。一切都是那么的有条不紊!

    所以,安排合适的人去做合适的事情,在实际工作中是非常重要的。这映射到并发编程领域也是同样的道理。如果将所有的任务交给一个线程执行,就好比将公司的所有事情交给你一个人去做一样。等到把事情做完了,黄花菜也凉了。所以,在并发编程中,我们同样需要将任务进行拆解,分工给合适的线程去完成。

    在并发编程领域,还需要注意一个问题就是:分工给合适的线程去做。 也就是说,应该主线程执行的任务不要交给子线程去做,否则,是解决不了问题的。这就好比一家公司的CEO将如何规划公司的未来交给一个产品开发人员去做一样,这不仅不能规划好公司的未来,甚至会与公司的价值观背道而驰。

    在JavaSDK中的:Executor、Fork/Join和Future都是实现分工的一种方式。

    同步

    在并发编程中的同步,主要指的是一个线程执行完任务后,如何通知其他的线程继续执行,强调的是性能。

    将任务拆分,并且合理的分工给了每个人,接下来就是如何同步每个人的任务了。

    假设小明是一名前端开发人员,他渲染页面的数据需要等待小刚的接口完成,而小刚写接口又需要等待小李的服务开发完成。也就是说,任务之间是存在依赖关系的,前面的任务完成后,才能进行后面的任务。

    对于实际工作中,这种任务的同步,大多数靠的是人与人之间的沟通,小李的服务写完了,告诉小刚,小刚则马上进行接口开发,等小刚的接口开发完成后,又告诉了小明,小明马上调用接口将返回的数据渲染在页面上。

    这种同步机制映射到并发编程领域,就是一个线程的任务执行完毕之后,通知其他的后续线程执行任务。

    对于这种线程之间的同步,我们可以使用下面的 if 伪代码来表示。

    if(前面的任务完成){
        执行当前任务
    }else{
        继续等待前面任务的执行
    }
    

    如果为了更能够及时的判断出前面的任务是否已经完成,我们也可以使用 while 伪代码来表示。

    while(前面的任务未完成){
        继续等待前面任务的执行
    }
    执行当前任务
    

    上述伪代码表示的意义是相同的:当线程执行的条件不满足时,线程需要继续等待,一旦条件满足,就需要唤醒等待的线程继续执行。

    在并发编程领域,一个典型的场景就是生产者-消费者模型。当队列满时,生产者线程需要等待,队列不满时,需要唤醒生产者线程;当队列为空时,消费者线程需要等待,队列不空时,需要唤醒消费者。我们可以使用下面的伪代码来表示生产者-消费者模型。

    • 生产者
    while(队列已满){
        生产者线程等待
    }
    唤醒生产者
    
    • 消费者
    while(队列为空){
        消费者等待
    }
    唤醒消费者
    

    在Java的SDK中,提供了一些实现线程之间同步的工具类,比如说:CountDownLatch、 CyclicBarrier 等。

    互斥

    同一时刻,只允许一个线程访问共享变量,强调的是线程执行任务的正确性。

    在并发编程领域,分工和同步强调的是执行任务的性能,而线程之间的互斥则强调的是线程执行任务的正确性,也就是线程的安全问题。如果多个线程同时访问同一个共享变量,则可能会发生意想不到的后果,而这种意想不到的后果主要是由线程的可见性、原子性和有序性问题产生的。而解决可见性、原子性和有序性问题的核心,就是互斥。

    关于互斥,我们可以用现实中的一个场景来描述:多个岔路口的车辆需要汇入一条道路中,而这条道路一次只能允许通过一辆车,此时,车辆就需要排队依次进入路口。

    Java中提供的synchronized、Lock、ThreadLocal、final关键字等都可以解决互斥的问题。

    例如,我们以synchronized为例来说明如何进行线程间的互斥,伪代码如下所示。

    //修饰方法
    public synchronized void xxx(){
        
    }
    //修饰代码块
    public void xxx(){
        synchronized(obj){
            
        }
    }
    //修饰代码块
    public void xxx(){
        synchronized(XXX.class){
            
        }
    }
    //修饰静态方法
    public synchronized static void xxx(){
        
    }
    

    总结

    并发编程旨在最大限度的利用计算机的资源,提高程序执行的性能,这需要线程之间的分工和同步来实现,在保证性能的同时,又需要保证线程的安全,这就又需要保证线程之间的互斥性。而并发编程的难点问题,往往又是由可见性、原子性和有序性问题导致的。所以,我们在学习并发编程时,一定要先弄懂线程之间的分工、同步和互斥。

    重磅福利

    微信搜一搜【冰河技术】微信公众号,关注这个有深度的程序员,每天阅读超硬核技术干货,公众号内回复【PDF】有我准备的一线大厂面试资料和我原创的超硬核PDF技术文档,以及我为大家精心准备的多套简历模板(不断更新中),希望大家都能找到心仪的工作,学习是一条时而郁郁寡欢,时而开怀大笑的路,加油。如果你通过努力成功进入到了心仪的公司,一定不要懈怠放松,职场成长和新技术学习一样,不进则退。如果有幸我们江湖再见!

    另外,我开源的各个PDF,后续我都会持续更新和维护,感谢大家长期以来对冰河的支持!!

  • 相关阅读:
    ini_set /ini_get函数功能-----PHP
    【转】那个什么都懂的家伙
    word 2007为不同页插入不同页眉页脚
    August 26th 2017 Week 34th Saturday
    【2017-11-08】Linux与openCV:opencv版本查看及库文件位置等
    August 25th 2017 Week 34th Friday
    August 24th 2017 Week 34th Thursday
    August 23rd 2017 Week 34th Wednesday
    August 22nd 2017 Week 34th Tuesday
    August 21st 2017 Week 34th Monday
  • 原文地址:https://www.cnblogs.com/binghe001/p/13849957.html
Copyright © 2011-2022 走看看