【高并发】学好并发编程,关键是要理解这三个核心问题

写在前面

写【高并发专题】有一段时间了,一些读者朋友留言说,并发编程很难,学习了不少的知识,可是在实际工做中却无从下手。对于一个线上产生的并发问题,又不知产生这个问题的缘由到底是什么。对于并发编程,感受上彷佛是掌握了,可是真正用起来却不是那么回事!前端

其实,形成这种现象的本质缘由就是没有透彻的理解并发编程的精髓,而学好并发编程的关键是须要弄懂三个核心问题:分工、同步和互斥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,后续我都会持续更新和维护,感谢你们长期以来对冰河的支持!!

相关文章
相关标签/搜索