Java并发0:并发问题核心点


Java并发0:并发问题核心点

什么是并发/并行?

同一时间段内,多个线程交替执行,为并发。同一时刻内,多个线程同时执行,为并行。

并发问题核心

并发问题核心为三点:分工,同步,互斥

  • 分工,即如何更好的拆解任务分配给线程以实现高效执行。

  • 同步,即线程之间如何高效协作。

  • 互斥,即保证同一时刻只允许一个线程访问共享资源。

Java SDK 并发包很大部分内容都是按照这三个维度组织的,例如 Fork/Join 框架就是一种分工模式,CountDownLatch 就是一种典型的同步方式,而可重入锁则是一种互斥手段。

分工

并发领域里,分工很重要,决定了并发性能。Java SDK 并发包里的 ExecutorFork/JoinFuture 本质上都是一种分工方法。除此之外,并发编程领域还总结了一些设计模式,基本上都是和分工方法相关的,例如生产者 - 消费者Thread-Per-MessageWorker Thread 模式等都是用来指导如何分工的。

同步

在并发编程领域里的同步,主要指的就是线程间的协作,本质上和现实生活中的协作没区别,不过是一个线程执行完了一个任务,如何通知执行后续任务的线程开工而已。

工作中遇到的线程协作问题,基本上都可以描述为这样的一个问题:当某个条件不满足时,线程需要等待,当某个条件满足时,线程需要被唤醒执行

在 Java 并发编程领域,解决协作问题的核心技术是管程。管程是一种解决并发问题的通用模型,除了能解决线程协作问题,还能解决互斥问题。可以这么说,管程是解决并发问题的万能钥匙

理论上,理解管程模型。实践上,掌握 Java SDK 并发包的工具类的应用场景和使用方法。

互斥

分工、同步主要强调的是性能,互斥,用专业术语叫“线程安全”,为了保证正序执行结果正确性。

所谓互斥,指的是同一时刻,只允许一个线程访问共享变量。

实现互斥的核心技术就是锁,Java 语言里synchronized、SDK 里的各种 Lock 都能解决互斥问题。

锁解决了安全性问题,但同时也带来了性能问题,如何保证安全性的同时又尽量提高性能?

  • 分场景优化,如Java SDK 里提供的 ReadWriteLockStampedLock 可以优化读多写少场景下锁的性能。
  • 使用无锁的数据结构,例如 Java SDK 里提供的原子类都是基于无锁技术实现的。
  • 其他方案,原理是不共享变量或者变量只允许读。这方面,Java 提供了 Thread Local final 关键字,还有一种 Copy-on-write 的模式。使用锁除了要注意性能问题外,还需要注意死锁问题。

全景图

并发编程全景图

别人的总结

从性能角度讲,为了提高执行一定计算机任务的效率,所以IO等待的时候不能让cpu闲着。

因此我们把任务拆分交替执行,有了分时操作系统,出现了并发,后来cpu多核了又有了并行计算。

这就是分工

分工后为了进一步提升效率和更加灵活地达到目的,要对任务进行组织编排,也就是对线程组织编排。

于是线程之间需要通信,于是操作系统提供了一些让进程,线程之间通信的方式。

这就是同步

但是事物总不是完美的。并发和通信带来了较高的编程复杂度,同时也出现了多线程并发操作共享资源的问题。于是天下大势,分久必合,我们又要将对共享资源的访问串行化。所以根据现实世界的做法设计了了信号量等等来补充这套体系。

这就是互斥

综上,这一切均为提高性能的手段和对其所产生问题的解决方案。

——常江舟

  1. 方法论层面:跳出来,看全景钻进去,看本质,这两条方法论,适合很多领域的学习的。
  2. 并发领域的全景图
    对于全景图,我之前也有一直在构建,可是因为知识储备不够,确实很难构建出来。稍微了解过并发领域知识的人都知道,里面的知识点、概念多而散:线程安全同步异步阻塞非阻塞死锁队列(为什么并发要跟队列扯上关系)、闭锁信号量活锁等等。如果单个去学这些知识点,单个去练习,如果没有「主线」,后期很容易忘。我思考再思考,也总结了一下学习并发的主线:
    首先,得理解并发的重要性,为什么需要并发?对于这个问题,只需要放在潜意识里面,只需要两个字:性能!其它的细节,再去慢慢拓展。
    然后,既然并发很重要,而并发处理的是任务,接下就是:对任务的抽象、拆解、分工执行。而线程模型,只是其中的一种模型,还有多进程、协程。Java使用的是多线程模型,对应到具体的代码就是:Thread, Runnable, Task,执行任务有:Exectors。 引出了线程,有势必存在着线程安全性的问题,因为多线程访问,数据存在着不一致的问题。
    再然后,大的任务被拆解多个小的子任务,小的子任务被各自执行,不难想象,子任务之间肯定存在着依赖关系,所以需要协调,那如何协调呢?也不难想到,锁是非常直接的方式(Monitor原理),但是只用锁,协调的费力度太高,在并发的世界里面,又有了一些其它的更抽象的工具:闭锁、屏障、队列以及其它的一些并发容器等;好了,协调的工作不难处理了。可是协调也会有出错的时候,这就有了死锁、活锁等问题,大师围绕着这个问题继续优化协调工具,尽量让使用者不容易出现这些活跃性问题;
    到此,「并发」的历史还在演化:如果一遇到并发问题,就直接上锁,倒也没有什么大问题,可是追求性能是人类的天性。计算机大师就在思考,能不不加锁也能实现并发,还不容易出错,于是就有了:CAScopy-on-write等技术思想,这就是实现了无锁并发;
    可是,事情到此还没有完。如果以上这些个东西,都需要每个程序员自己去弄,然后自己保证正确性,那程序员真累死了,哪还有时间、精力创造这么多美好的应用!于是,计算机大师又开始思考,能不能抽象出统一模型,可能这就有了类似于Java内存模型这样的东西。

——Jerry银银


文章作者: Wendell
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Wendell !
评论
  目录