回到过去,电脑有一个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上同时(不是真正的同时,而是看来是同时,因为cpu要在多个程序间切换)运行多个程序。

并行,是每个cpu运行一个程序。

打个比方。并发,就像一个人(cpu)喂2个孩子(程序),轮换着每人喂一口,表面上两个孩子都在吃饭。并行,就是2个人喂2个孩子,两个孩子也同时在吃饭。

多线程的状态

初始状态(new)

new出来一个实例,就进入了初始状态

可运行/就绪状态(Runnable)

  • 调用线程的start()方法,此线程进入可运行状态。
  • 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入可运行状态。
  • 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入可运行状态。
  • 锁池里的线程拿到对象锁后,进入可运行状态。

当前线程具备了运行条件,但调度程序还没有调度到。

运行状态(Running)

就绪状态的线程获取了CPU,执行程序代码。

阻塞状态(Blocked)

线程因为某种原因释放了运行资源,暂停运行。只有线程再次变为可运行状态,才可能再次运行。

  • 执行wait()方法,释放锁资源
  • 在获取锁资源时,被其他线程占用,处于等待状态
  • 运行sleep()或者join()方法

死亡状态(dead)

线程执行完了run()方法,或者执行run()方法过程中异常退出,或者主线程执行完毕退出。

Java并发/多线程系列——初识篇的更多相关文章

  1. java并发编程系列原理篇--JDK中的通信工具类Semaphore

    前言 java多线程之间进行通信时,JDK主要提供了以下几种通信工具类.主要有Semaphore.CountDownLatch.CyclicBarrier.exchanger.Phaser这几个通讯类 ...

  2. Java并发/多线程系列——线程安全篇(1)

    创建和启动Java线程 Java线程是个对象,和其他任何的Java对象一样.线程是类的实例java.lang.Thread,或该类的子类的实例.除了对象之外,java线程还可以执行代码. 创建和启动线 ...

  3. 原创】Java并发编程系列2:线程概念与基础操作

    [原创]Java并发编程系列2:线程概念与基础操作 伟大的理想只有经过忘我的斗争和牺牲才能胜利实现. 本篇为[Dali王的技术博客]Java并发编程系列第二篇,讲讲有关线程的那些事儿.主要内容是如下这 ...

  4. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  5. Java多线程系列--“基础篇”10之 线程优先级和守护线程

    概要 本章,会对守护线程和线程优先级进行介绍.涉及到的内容包括:1. 线程优先级的介绍2. 线程优先级的示例3. 守护线程的示例 转载请注明出处:http://www.cnblogs.com/skyw ...

  6. Java多线程系列--“基础篇”11之 生产消费者问题

    概要 本章,会对“生产/消费者问题”进行讨论.涉及到的内容包括:1. 生产/消费者模型2. 生产/消费者实现 转载请注明出处:http://www.cnblogs.com/skywang12345/p ...

  7. Java多线程系列--“基础篇”04之 synchronized关键字

    概要 本章,会对synchronized关键字进行介绍.涉及到的内容包括:1. synchronized原理2. synchronized基本规则3. synchronized方法 和 synchro ...

  8. Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式

    概要 本章,我们学习“常用的实现多线程的2种方式”:Thread 和 Runnable.之所以说是常用的,是因为通过还可以通过java.util.concurrent包中的线程池来实现多线程.关于线程 ...

  9. Java多线程系列--“基础篇”03之 Thread中start()和run()的区别

    概要 Thread类包含start()和run()方法,它们的区别是什么?本章将对此作出解答.本章内容包括:start() 和 run()的区别说明start() 和 run()的区别示例start( ...

随机推荐

  1. STM32经典概述(干货 )

    STM32经典概述(干货 ) 首先,在学习Cortex-M3时,我们必须要知道必要的缩略语.  在网上看的,觉得挺好的,分享过来了 整理如下: AMBA:先进单片机总线架构   ADK:AMBA设计套 ...

  2. github如何添加ssh

    1.运行git Bash 输入如下命令: $ cd ~/.ssh $ ls 输入这2个命令 ,我们可以看到 id_rsa.pub 或 id_dsa.pub 这2个文件已经存在了,id_rsa 是私钥, ...

  3. Spring-MVC开发步骤(入门配置)

    Spring-MVC开发步骤(入门配置) Step1.导包 spring-webmvc Step2.添加spring配置文件 Step3.配置DispatcherServlet 在web.xml中: ...

  4. javascript中this的指向

    作为一个前端小白在开发中对于this的指向问题有时候总是会模糊,于是花时间研究了一番. 首先this是JS的关键字,this是js函数在运行是生成的一个内部对象,生成的这个this只能在函数内部使用. ...

  5. Go数组、切片、映射的原理--简明解析

    数组.切片.映射是Golang的最重要的数据结构,下面是对这3种数据结构的一点个人总结: 一.数组 数组是切片和映射的基础数据结构. 数组是一个长度固定的数据类型,存储着一段具有相同数据类型元素的连续 ...

  6. poj 3613 floyd + 快速幂

    题意:本题的大意就是问从S 到 T 经过边得个数恰为k的最短路是多少. 思路:对于邻接矩阵每一次floyd求的是每个点间的最短距离,则n次floyd就是每个点间n条路的最短距离(可以重复边); 但是由 ...

  7. Nhibernate学习教程(2)-- 第一个NHibernate程序

    NHibernate之旅(2):第一个NHibernate程序 本节内容 开始使用NHibernate 1.获取NHibernate 2.建立数据库表 3.创建C#类库项目 4.设计Domain 4- ...

  8. CCIE-MPLS VPN-实验手册(上卷)

    看完了看完了看完了,豪爽豪爽豪爽,一个月了,写得挺棒.总共14个mpls vpn的实验,为留下学习的痕迹,原封不动献出. CCIE实验手册 (路由部分-MPLSVPN基础篇) [CCIE]  JUST ...

  9. 设计模式-单体模式(C++)

    设计模式-单体模式 单体模式在使用非常方便,适合于单一的对象,例如全局对象的抽象使用. 需要注意的是单体模式不可继承 // 实现 Singleton.h #ifndef __SINGLETON_H__ ...

  10. 201521123095 《Java程序设计》第8周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 Q1 List中指定元素的删除(题目4-1) 1.1 实验总结 对于删除函数 ...