继上一本《深入理解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. 对JavaScript中闭包的理解

    在前端开发中闭包是一个很重要的知识点,是面试中一定会被问到的内容.之前我对闭包的理解主要是"通过闭包可以在函数外部能访问到函数内部的变量",对闭包运用的也很少,甚至自己写过闭包自己 ...

  2. javascript中的异步编程

    正常情况下js都是顺序执行的,但是也有很多场景下实际上是异步操作: 1.定时器都是异步操作 2.事件绑定都是异步操作 3.AJAX中一般我们都采取异步操作(也可以同步) 4.回调函数可以理解为异步(不 ...

  3. Nginx 错误日志配置

    1.Nginx错误日志信息介绍: error_log的语法格式及参数说明: error_log      file         level; 关键字    日志文件   错误日志级别 其中,关键字 ...

  4. centos-7 虚拟机安装图形界面

    centos-7 虚拟机安装图形界面 想到安装一个docker环境,于是拿出了以前装的虚拟机centos7,记得装完后,没进行任何配置(默认安装的是命令行界面). 配置网络 现有的虚拟机是没有办法联网 ...

  5. 添加RPMfusion仓库

    先添加epel Fedora的意识形态很是严谨,它不会自带任何非自由组件.官方仓库不会提供一些包含有非自由组件的基本软件,比如像多媒体编码.因此,安装一些第三方仓库很有必要,这些仓库会为我们提供一些基 ...

  6. PHP LAMP环境搭建及网站配置流程(完整版)

    心血来潮想做一个自己的博客网站,写一些文章做技术分享,平时遇到的一些问题的解决办法都记录下来,网站搭建成功,那么第一篇博客自然就是整个网站的搭建以及域名的注册.备案.解析流程,总共分为以下几步: 1. ...

  7. MarkDown 排版测试

    大标题 小标题(正常) 小标题(多一杠) 一级标题 二级标题 三级标题 四级标题(未空格) 四级标题(正常) 个人编程,写一个命令行程序 注册Github账号,建立项目仓库 添加ReadMe.md并编 ...

  8. Ignatius and the Princess III(方案背包+搜索)

    就是问你,n这个数可以被多少种方案组成. 比如: 算是,方案+完全背包的模板题了. #include<iostream> #include<cstring> using nam ...

  9. 路由器不重启,是否ip就永远不变

    今天发现公司的公网ip突然变了,没有人去动过路由器怎么会这样呢?经查原因如下:1.不一定,IP变化是每一次拨号重新获取的.2.路由器重启了,会自动拨号,获得IP3.但如果说因各种原因,掉线,路由器也会 ...

  10. Redis和Memcached的一些区别

    我们都知道,把一些热数据存到缓存中可以极大的提高速度,那么问题来了,是用Redis好还是Memcached好呢,以下是它们两者之间一些简单的区别与比较: 1. Redis不仅支持简单的k/v类型的数据 ...