继上一本《深入理解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. FUSE 文件系统 example部分 源码注释 (libfuse 2.9.9)

    本篇文章主要是针对fuse-2.9.9 Example 部分 给出的源码,结合官方文档,以及网上的资料给出注释,希望能给正在学习的你们一点帮助. Hello.c /* FUSE: Filesystem ...

  2. C#中的console类输入输出功能

    Console.WriteLine() 直接将括号内内容显示在控制台界面中(相当于C语言printf()吧) Console.ReadLine()获取控制台用户自己输入的内容(功能和C语言scanf( ...

  3. Navicat连接Oracle的几个问题及解决方案

    1.用Navicat连接Oracle数据库时报错ORA-28547:connection to server failed,probable Oracle Net admin error 解决方案: ...

  4. 如何以SYSTEM用户运行CMD

    有的时候有些文件在管理员账户不能删除,这个时候需要在SYSTEM用户下删除. 可以通过以SYSTEM权限运行CMD来删除某些文件或目录的目的. 1. 从微软网站下载PSTool. 2. 以管理员运行C ...

  5. 14LaTeX学习系列之---LaTeX的浮动体

    目录 目录 前言 (一)浮动体的基础知识 1.环境及语法 2.允许位置的参数 3.其他命令 (二)实例: 1.源代码 2.输出效果 (三)浮动体的高级操作 1.标题的控制 2.并排与子图表 3.绕排 ...

  6. 个人技术博客--团队Git规范(参考西瓜学长)

    援引西瓜学长:GitHub团队项目合作流程 废话少说直接写 1.fork 1.对于组员来说第一步就是fork 2.点击fork之后 上面是我们的团队仓库 切换回自己的仓库 就会看到 是fork于团队仓 ...

  7. mybatis的xml映射文件

    1,在进行统计查询时候,不想写映射的实体类,这时候设置返回的resultType类型是map <select id="getMap" resultType="jav ...

  8. salt安装及使用部分笔记

    安装脚本install_salt.sh: #!/bin/bash####安装第三方yum源rpm -Uvh http://ftp.linux.ncsu.edu/pub/epel/6/i386/epel ...

  9. Thinkphp5.0整合个推例子

    最近做一个后台发送消息推送到app(android和ios)的功能,该功能采用的是个推接口,基于php的,我用TP5来实现这个推送流程.先看官方demo吧.可以先参考官方给到的例子来看http://d ...

  10. PID控制本版一 (M100可用)

    版本1 云台+无人机 https://en.wikipedia.org/wiki/PID_controller https://github.com/tekdemo/MiniPID 详细讲解 PIDC ...