继上一本《深入理解Java虚拟机》之后,学习计划里的另一本书《Java并发编程实战》现在开始学习,并记录学习笔记。

第一章主要内容是介绍 并发 的简介、发展、特点。

编写正确的程序很难,而编写正确的并发程序则难上加难。与串行程序相比,在并发程序中存在更多容易出错的地方。那么,为什么还要编写并发程序?线程是Java语言中不可或缺的重要功能,它们能使复杂的异步代码变得更简单,从而极大地简化了负责系统的开发。此外,要想充分发挥多处理器系统的强大计算能力,最简单的方式就是使用线程。随着处理器数量的持续增长,如何高效地使用并发正变得越来越重要。

1.1  并发简史

  随着计算机的发展,在计算机中加入操作系统来实现多个程序的同时执行,主要是基于以下原因:

  • 资源利用率:在某些情况下,程序必须等待某个外部操作执行完成,而在等待时程序无法执行其他任何工作。因此,在等待的同时可以运行另一个程序,这无疑将提高资源的利用率。
  • 公平性:不同用户和程序对于计算机上的资源有着同等的使用权。一种高效的运行方式是通过粗粒度的时间分片(Time Slicing)使这些用户和程序能共享计算机资源,而不是由一个程序从头运行到尾,再启动下一个程序。
  • 便利性:在计算多个任务时,应该编写多个程序,每个程序执行一个任务并在必要时相互通信,这比只编写一个程序来计算所有任务更容易实现。

  在早期的分时系统中,每个进程相当于一台虚拟的冯`诺依曼计算机,根据机器语言的语义以串行的方式执行指令,对每条执行的指令,都有相应的“下一条指令”,程序中的控制流是按照指令集的规则来确定的。

  线程允许在同一个进程中同时存在多个程序控制流。线程会共享进程范围内的资源,例如内存句柄和文件句柄,但每个线程都有各自的程序计数器(Program Counter)、栈以及局部变量等。

1.2  线程的优势

  如果使用得当,线程可以有效地降低程序的开发和维护等成本,同时提升复杂应用程序的性能。

  1.2.1  发挥多处理器的强大能力

    多处理器系统的日益普及,是线程发展的重要因素。由于基本的调度单位是线程,因此如果在程序中只有一个线程,那么最多同时只能在一个处理器上运行。

    使用多个线程还有助于在单处理器系统上获得更高的吞吐率。

  1.2.2  建模的简单性

    如果需要完成多种类型的任务,那么需要管理不同任务之间的优先级和执行时间,并在任务之间进行切换,这将带来额外的开销,对于线程同样如此。

    我们可以通过一些现有的框架来实现多线程,例如 Servlet 和 RMI(Remote Method Invocation)。框架负责解决一些细节问题,例如请求管理、线程创建、负载平衡,并在正确的时刻将请求分发给正确的应用程序组件。

  1.2.3  异步事件的简化处理

    服务器应用程序在接受来自多个远程客户端的套接字连接请求时,如果为每个连接都分配其各自的线程并且使用同步 I/O ,那么就会降低这类程序的开发难度。

    早期的操作系统通常会将进程中可创建的线程数量限制在一个较低的阈值内,线程创建数量受 操作系统 和 JVM 参数设置影响。

    Java 类库需要获得一组实现非阻塞 I/O 的包(java.nio)。

  1.2.4  响应更灵敏的用户界面

    如果某事件线程中的任务需要很长的执行时间,例如对一个大型文档进行拼写检查,或者从网络上获取一个资源,那界面的响应灵敏度就会降低。如果用用在执行这类任务时触发了某个动作,那么必须等待很长时间才能获得响应,因为事件线程要先执行玩该任务。更糟糕的是,不仅仅界面失去了响应,而且即使在界面上包含了“取消”按钮,也无法取消这个长时间执行的任务,因为事件线程只有在执行完该任务后才能响应“取消”按钮的地址事件。然而,如果将这个长时间运行的任务放在一个单独的线程中运行,那么事件线程就能及时地处理界面事件,从而使用户具有更高的灵敏度。

1.3  线程带来的风险

  Java 对线程的支持其实是一把双刃剑。当线程还是一项鲜为人知的技术时,并发性是一个“高深地”主题,但现在,主流开发人员都必须了解线程方面的内容。

  1.3.1  安全性问题

    线程安全性可能是非常复杂的,在没有充足同步的情况下,多个线程中的操作执行顺序是不可预测的,甚至会产生奇怪的结果。看下面的例子,在单线程可以正确工作,但在多线程环境下则不能。  

public class UnsafeSequence {              【冷漠脸--非最优方式】
private int value; public int getValue() {
return value++; //返回当前数组,并自加
}
}

    UnsafeSequence 的问题在于,如果执行时机不对,那么两个线程的调用getValue 时会得到相同的值。

    实际上, value++ 包含了三个独立的操作:读取value,将value+1,并将计算结果写入 value。

    由于运行时可能将多个线程之间的操作交替执行,因此这两个线程可能同时执行读操作,从而使他们得到相同的值,并且都自加1。

    UnsafeSequence 类中说明的一种常见的并发安全问题,称为 竞态条件(Race Condition)。由于多个线程要共享相同的内存地址空间,并且是并发运行,因此他们可能会访问或修改其他线程正在使用的变量。当然,这是一种极大的便利,因为这种方式比其他          线程间通信机制更容易实现数据共享。但它同样也带来了巨大的风险:线程会由于无法预料的数据变化而发生错误。

    对此,我们可以将 getValue() 方法修改为一个同步方法。

public class UnsafeSequence {
private int value; public synchronized int getValue() { //同步方法
return value++;
}
}

  1.3.2  活跃性问题

    安全性不仅对于多线程很重要,对于单线程程序同样重要。线程还会导致一些在单线程程序中不会出现的问题,例如活跃性问题。

    安全性的含义是“永远不会发生糟糕的事情”,而活跃性则关注于另一个目标,即“某件正确的事情最终会发生”。

    当某个操作无法继续执行下去时,就会发生活跃性问题。在串行程序中,活跃性问题的形式之一就是无意中造成的无限循环,从而使循环之后的代码无法得到执行。

  1.3.3  性能问题

    性能问题包括多个方面,例如服务时间过长,响应不灵敏,吞吐率过低,资源消耗过高,或者可伸缩性较低等。与安全性和活跃性一样,在多线程程序中不仅崔仔与单线程相同的性能问题,而且还存在由于使用线程而引入的其他性能问题。

    在涉及良好的并发应用程序中,线程能提高程序的性能,但无论如何,线程总会带来某种程序的运行时开销。在多线程程序中,当线程调度器临时挂起活跃线程并转而运行另一个线程时,就会频繁地出现上下文切换操作(Context Switch),这种操作将带来极大        的开销:保存和恢复执行上下文,丢失局部性,并且CPU 时间将更多地花在线程调度而不是线程运行上。当线程共享数据时,必须使用同步机制,而这些机制往往会抑制某些编译器优化,使内存缓存区中的数据无效,以及增加共享内存总线的同步流量。

1.4  线程无处不在

  即使在程序中没有显式地创建线程,但在框架中仍可能会创建线程,因此在这些线程中调用的代码同样必须是线程安全的。

  每个 Java 应用程序都会使用线程。当JVM 启动时,它将为 JVM 内部任务(例如,垃圾收集、终结操作等)创建后台线程,并创建一个主线程来运行 main 方法。AWT(Abstract Window Toolkit)和Swing 的用户界面框架将创建线程来管理用户界面事件。Timer 将创建线程来执行延迟任务。一些组件框架,例如 Servlet 和 RMI,都会创建线程池并调用这些线程中的方法。

【Java并发.1】简介的更多相关文章

  1. Java并发理论简介

    这些文字来自于Java程序员修炼之道,记录一下 一. java线程模型 Java线程模型建立在两个基本概念之上 共享的,默认可见的可变状态 抢占式线程调度 我们从侧面思考一下这两个概念 所有线程可以很 ...

  2. java 并发——CountDownLatch

    java 并发--CountDownLatch 简介 public class CountDownLatch { private final Sync sync; private static fin ...

  3. java 并发——ReentrantLock

    java 并发--ReentrantLock 简介 public class ReentrantLock implements Lock, java.io.Serializable { // 继承了 ...

  4. java 并发——AbstractQueuedSynchronizer

    java 并发--AbstractQueuedSynchronizer 简介 abstract class AbstractQueuedSynchronizer extends AbstractOwn ...

  5. java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四)

    Lock接口提供了方法Condition newCondition();用于获取对应锁的条件,可以在这个条件对象上调用监视器方法 可以理解为,原本借助于synchronized关键字以及锁对象,配备了 ...

  6. java 并发多线程显式锁概念简介 什么是显式锁 多线程下篇(一)

    目前对于同步,仅仅介绍了一个关键字synchronized,可以用于保证线程同步的原子性.可见性.有序性 对于synchronized关键字,对于静态方法默认是以该类的class对象作为锁,对于实例方 ...

  7. 《Java并发编程实战》读书笔记一 -- 简介

    <Java并发编程实战>读书笔记一 -- 简介 并发的历史 并发的历史,也是人类利用有限的资源去提高生产效率的一个的例子. 设想现在有台计算机,这台计算机具有以下的资源: 单核CPU一个 ...

  8. java并发编程笔记(一)——并发编程简介

    java并发编程笔记(一)--简介 线程不安全的类示例 public class CountExample1 { // 请求总数 public static int clientTotal = 500 ...

  9. 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...

随机推荐

  1. 洗礼灵魂,修炼python(51)--爬虫篇—变色龙般的伪装

    变色龙原理 变色龙这种动物想必大家都了解,它们会根据周遭环境的局势来改变自己的颜色,伪装自己. 那么爬虫有这种技能吗?当然是有的,先不着急说这个问题. 从上一篇开始,你有没有想过,站在网站管理的角度, ...

  2. python第十六天,昨天来晚了,作业终于完成了

    作业 1: 员工信息表程序,实现增删改查操作 可进行模糊查询,语法至少支持下面3种: select name,age from staff_table where age > 22 select ...

  3. Sql_server基本操作

    使用Sql_server创建表,视图,触发器,存储过程,函数等基本操作. create table test1( /* 创建一个表 */ num int ) alter table test1 /* ...

  4. sqlserver序列定时初始化

    1.创建序列 2.序列初始化存储过程 create procedure proDemo as begin alter sequence dbo.序列名 restart with 0; end 3.创建 ...

  5. 用友U8年度账结转 常用凭证丢失

    用友年度账结转常用凭证丢失解决方法 1.将新年度账两个表备份后清空:gl-bfreq和gl-bfreqinfo 2.复制旧年度账中gl-bfreq和gl-bfreqinfo两个表数据到新年度账即可.

  6. 使用Maven分环境打包:dev sit uat prod

    使用Maven管理的项目,经常需要根据不同的环境打不同的包,因为环境不同,所需要的配置文件不同,比如database的连接信息,相关属性等等. 在Maven中,我们可以通过P参数和profiles元素 ...

  7. 【NOIP2017D2T3】列队

    Description Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵.Sylvia所在的方阵中有n × m名学生,方阵的行数为 n, ...

  8. Android开发学习笔记(二)——编译和运行原理(1)

    http://www.cnblogs.com/Pickuper/archive/2011/06/14/2078969.html 接着上一篇的内容,继续从全局了解Android.在清楚了Android的 ...

  9. python 播放mp3

    import os os.system('out1.mp3') 自动带开播放器

  10. oracle 12c 12.1.0.2.0 BUG 22562145

    Wed May 23 17:46:14 2018TT01: Standby redo logfile selected for thread 1 sequence 42251 for destinat ...