Java并发/多线程系列——初识篇
回到过去,电脑有一个CPU,一次只能执行一个程序。后来多任务处理意味着计算机可以同时执行多个程序(AKA任务或进程)。这不是真的“同时”。单个CPU在程序之间共享。操作系统将在运行的程序之间切换,在切换之前执行每个程序一段时间。
随着多任务处理,软件开发人员面临新的挑战。程序不能再假定所有的CPU时间都可用,也不能使用所有内存或任何其他计算机资源。“good citizen”程序应该释放所有不再使用的资源,所以其他程序可以使用它们。
后来又来了多线程,这意味着你可以在同一个程序中有多个执行线程。执行线程可以被认为是执行程序的CPU。当您有多个线程执行相同的程序时,就像在同一程序中执行多个CPU。
多线程是可以提高某些类型程序性能的好方法。然而,多线程比多任务更具挑战性。线程在相同的程序中执行,就会同时读取和写入同一个存储器,这可能导致在单线程程序中看不到错误。在单CPU机器上可能看不到这些错误,因为两个线程从未真正“同时”的执行。现代电脑虽然配有多核CPU,甚至还有多个CPU。这意味着单独的线程可以由多个的内核或多个CPU同时执行。
如果一个线程在另一个线程写入内存时读取该位置的值,那么第一个线程会读取什么值?旧值?第二个线程写的值?还是两者之间的混合的一个值?或者,如果两个线程同时写入内存的同一位置,完成后会留下什么值?第一个线程写的值?第二个线程写的值?还是两个值的混合?
如果没有适当的预防措施,任何这些结果都是可能的,甚至是不可预测。结果可能会不时变化,因此,开发人员知道如何采取正确的预防措施,这就意味着学习控制线程如何访问内存,文件,数据库等共享资源是非常重要的。
多线程的优缺点
优点
- 更高的资源利用率
- 响应速度快
- 占用大量时间的任务可定期将处理器时间让给其它任务
- 可设置各个任务的优先级
缺点
- 对线程进行管理要求额外的 CPU开销。线程的使用会给系统带来上下文切换的额外负担。当这种负担超过一定程度时,多线程的特点主要表现在其缺点上,比如用独立的线程来更新数组内每个元素。
- 线程的死锁问题
- 多个线程同时操作同一资源,造成无法预知的错误
并发模型
Parallel Workers(并行工作)
第一个并发模型就是我所说的parallel worker(并行工作)模型。进入作业被分配给不同的工作人员。以下是并行工作并发模型的图示:
在并行工作并发模型中,委托者将传入的作业分配给不同的工作人员。每个工作人员完成了全部工作。工作并行工作,运行在不同的线程,可能在不同的CPU上。
并行工作并发模型是Java应用程序中最常用的并发模型(尽管正在发生变化)。java.util.concurrent 包中的许多并发实用程序都设计用于此模型。
并行工作的优点
要增加应用程序的并行化,只需添加更多的工作人员。实现简单,易上手。
并行工作的缺点
一旦共享状态进入并行工作并发模式,它就会变得复杂了。线程需要访问共享数据,以确保一个线程的更改对其他线程是可见的(推送到主内存,而不仅仅停留在执行线程的CPU的CPU缓存中)。 线程需要避免竞争条件, 死锁和许多其他共享状态并发问题。
共享状态可以由系统中的其他线程修改。因此,工作人员每次需要重新读取状态,以确保它正在处理最新的副本。无论共享状态是保存在内存还是外部数据库中,都是如此。内部不保持状态的工作者(但每次需要重新读取数据)被称为无状态。每次需要重新读取数据可能会变慢。特别是如果状态存储在外部数据库中。
并行工作者模型的另一个缺点是作业(job)执行顺序是非确定性的。没有办法保证首先或最后执行哪些工作。因此不能保证一个作业发生在另一个作业之前。
Assembly Line(流水线)
这些工人组织得像工厂里的工人一样。每个工作人员只执行完整工作的一部分。当该部分完成时,作业将作业转发到下一个工作人员。每个工人都在自己的线程中运行,与其他工作人员共享状态。 这也有时被称为共享的并发模式。
使用装配线并发模型的系统通常设计为使用非阻塞IO。非阻塞IO意味着当工作人员启动IO操作(例如从网络连接读取文件或数据)时,工作人员不会等待IO调用完成,IO操作速度很慢,所以等待IO操作完成是浪费CPU时间。CPU可能在做其他事情。当IO操作完成时,IO操作的结果(例如数据读取或写入的数据状态)传递给另一个工作。
实际上,这些工作可能不会沿着一条流水线流动。由于大多数系统可以执行多个作业,因此根据需要完成的任务,作业将从工作流程转移到工作。实际上,可能会有多个不同的虚拟装配线同时进行。
作业甚至可以转发给多个工作者进行并发处理。例如,作业可以转发给作业执行器和作业记录器。该图说明如何通过将其作业转发给同一个工作人员(中间装配线中的最后一个工作人员)来完成所有三条装配线的完成:
Actors vs. Channels(演员和通道)
actors和channels是两个类似的流水线模型的例子。
在演员模型中,每个工人都被称为演员。演员可以直接发送消息给对方。消息被异步发送和处理。演员可以用于实现一个或多个作业处理装配线,如前所述。以下是演示模型的图示:
在通道模式中,工作者之间不直接沟通。相反,他们在不同的通道上发布他们的消息(事件)。其他工作者可以在这些通道上收听消息,而不需要发送方知道谁正在收听。这是一个说明通道模型的图:
通道模式对我们来说似乎更加灵活。工作者不需要知道工作者稍后将在装配线中处理什么工作。它只需要知道什么通道转发作业(或发送消息等)。通道上的听众可以订阅并取消订阅,而不影响写入通道的工作者。这允许工人之间稍微松动的耦合。
流水线模型的优点
没有共享状态。工作者之间不用分享任何状态,因此无需考虑并发访问共享状态可能产生的所有并发问题。
有状态的工作者。由于工人知道没有其他线程修改其数据,所以工作人员可以是有状态的。他们可以保留他们需要在内存中操作的数据,只需将最后的外部存储系统进行更改。因此,有状态的工作者通常可能比无状态的工作者更快。
可实现工作订阅。
流水线模型的缺点
代码实现上会更加的复杂,代码的阅读性会降低。因为工作者的代码有时被写成回调处理程序员,有许多嵌套回调处理程序的代码可能会导致一些开发人员调用callback hell(回调地狱)。 callback hell只是意味着很难跟踪代码是在实际程序中是怎么调用的,以及确保每个回调都可以访问所需的数据。
并发性(concurrency)与并行性(parallellism)
术语并发性和并行性通常用于多线程程序。但并发和并行究竟是什么意思,而且是相同的术语还是什么?
简短的答案是“否”。它们不是相同的术语,尽管它们在表面上看起来非常相似。也花了我一些时间来终于找到并了解并发和并行性之间的区别。因此,我决定在这个Java并发教程中添加一个关于并发性与并行性的文本。
并发,是在同一个cpu上同时(不是真正的同时,而是看来是同时,因为cpu要在多个程序间切换)运行多个程序。
并行,是每个cpu运行一个程序。
打个比方。并发,就像一个人(cpu)喂2个孩子(程序),轮换着每人喂一口,表面上两个孩子都在吃饭。并行,就是2个人喂2个孩子,两个孩子也同时在吃饭。
多线程的状态
初始状态(new)
new出来一个实例,就进入了初始状态
可运行/就绪状态(Runnable)
- 调用线程的start()方法,此线程进入可运行状态。
- 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入可运行状态。
- 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入可运行状态。
- 锁池里的线程拿到对象锁后,进入可运行状态。
当前线程具备了运行条件,但调度程序还没有调度到。
运行状态(Running)
就绪状态的线程获取了CPU,执行程序代码。
阻塞状态(Blocked)
线程因为某种原因释放了运行资源,暂停运行。只有线程再次变为可运行状态,才可能再次运行。
- 执行wait()方法,释放锁资源
- 在获取锁资源时,被其他线程占用,处于等待状态
- 运行sleep()或者join()方法
死亡状态(dead)
线程执行完了run()方法,或者执行run()方法过程中异常退出,或者主线程执行完毕退出。
Java并发/多线程系列——初识篇的更多相关文章
- java并发编程系列原理篇--JDK中的通信工具类Semaphore
前言 java多线程之间进行通信时,JDK主要提供了以下几种通信工具类.主要有Semaphore.CountDownLatch.CyclicBarrier.exchanger.Phaser这几个通讯类 ...
- Java并发/多线程系列——线程安全篇(1)
创建和启动Java线程 Java线程是个对象,和其他任何的Java对象一样.线程是类的实例java.lang.Thread,或该类的子类的实例.除了对象之外,java线程还可以执行代码. 创建和启动线 ...
- 原创】Java并发编程系列2:线程概念与基础操作
[原创]Java并发编程系列2:线程概念与基础操作 伟大的理想只有经过忘我的斗争和牺牲才能胜利实现. 本篇为[Dali王的技术博客]Java并发编程系列第二篇,讲讲有关线程的那些事儿.主要内容是如下这 ...
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- Java多线程系列--“基础篇”10之 线程优先级和守护线程
概要 本章,会对守护线程和线程优先级进行介绍.涉及到的内容包括:1. 线程优先级的介绍2. 线程优先级的示例3. 守护线程的示例 转载请注明出处:http://www.cnblogs.com/skyw ...
- Java多线程系列--“基础篇”11之 生产消费者问题
概要 本章,会对“生产/消费者问题”进行讨论.涉及到的内容包括:1. 生产/消费者模型2. 生产/消费者实现 转载请注明出处:http://www.cnblogs.com/skywang12345/p ...
- Java多线程系列--“基础篇”04之 synchronized关键字
概要 本章,会对synchronized关键字进行介绍.涉及到的内容包括:1. synchronized原理2. synchronized基本规则3. synchronized方法 和 synchro ...
- Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式
概要 本章,我们学习“常用的实现多线程的2种方式”:Thread 和 Runnable.之所以说是常用的,是因为通过还可以通过java.util.concurrent包中的线程池来实现多线程.关于线程 ...
- Java多线程系列--“基础篇”03之 Thread中start()和run()的区别
概要 Thread类包含start()和run()方法,它们的区别是什么?本章将对此作出解答.本章内容包括:start() 和 run()的区别说明start() 和 run()的区别示例start( ...
随机推荐
- [WPF] UserControl vs CustomControl
介绍 WPF中有两种控件:UserControl和CustomControl,但是这两者有什么区别呢?这篇博客中将介绍两者之间的区别,这样可以在项目中合理的使用它们. UserControl 将多个W ...
- CountDownLatch和CyclicBarrier 特点比较
详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp79 并发编程中的CountDownLatch和CyclicBarri ...
- 第3阶段——内核启动分析之make uImage编译内核(3)
目标: 通过分析makefile,明白make uImage如何编译内核 把整个内核的makefile分成三类(makefile资料文档在linux-2.6.22.6/Documentation/bu ...
- IOS学习【前言】
2016-1-14 16年开始时导师安排任务,开始IOS学习之旅 经过几天的学习,感觉还是需要作比较多的学习笔记,因此开始用博客记录整个过程,方便以后查看学习与分享. 主要记录一些关键的问题处理方法 ...
- 全平台轻量级 Verilog 编译器 & 仿真环境
一直苦于 modelsim 没有Mac版本,且其体量过大,在学习verilog 时不方便使用. 终于找到一组轻量级且全平台 ( Linux+Windows+macOS ) 的编译仿真工具组. Icar ...
- JavaScript 的使用基础总结③
JavaScript 中的对象 JavaScript 中的所有事物都是对象:字符串.数值.数组.函数... JavaScript 允许自定义对象. (一)数组 数组对象的作用是:使用单独的变量名来存储 ...
- 分而治之(Work Breakdown Structure, WBS)
不知道大家有没有和我一样的情况,就是想写一篇博客,不知道从何写起,如何组织语言,如何安排这篇博客的要交待的事情的前因后果:如果在写作过程中被打断,又不知道如何重新拾起键盘,从哪里写起."就如 ...
- 201521123042《Java程序设计》 第7周学习总结
1. 本周学习总结 网上看了很多资料,发现这一张图总结的还不错就引用过来了.但是最上面的Map和Collection之间的关系应该是依赖,不是Produces. ①概述:Java集合框架主要包括两种类 ...
- 201521123062《Java程序设计》第7周学习总结
1. 本周学习总结 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 源代码如下: public boolean contains(Object ...
- 201521123027 《JAVA程序设计》第五周学习总结
1.本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容 Comparable接口与Comparator接口的区别: Markdown的其他用 ...