离开博客园很久了,自从找到工作,到现在基本没有再写过博客了。在大学培养起来的写博客的习惯在慢慢的消失殆尽,感觉汗颜。所以现在要开始重新培养起这个习惯,定期写博客不仅是对自己学习知识的一种沉淀,更是在督促自己要不断的学习,不断的进步。

最近在进一步学习Java并发编程,不言而喻,这部分内容是很重要的。现在就以《并发编程的艺术》一书为主导线,开始新一轮的学习。

进程和线程

进程是一个应用程序在处理机上的一次执行过程,线程是进程的最小基本单位(个人理解)。一个进程可以包含多个线程。

上下文切换

我们都知道,即使是单核处理器也支持多线程,CPU通过时间片分配算法来给每个线程分配时间让线程得以执行,因为时间片非常短,所以在用户角度来讲,会感觉多个线程是在同时执行。那什么是上下文切换呢?举个例子,当线程A执行到某一步时,此时CPU将时间让给了线程B进行执行,那么在进行切换的时候,系统一定要保存此时此刻线程A所执行任务的状态,比如执行到哪里、运行时的参数等,那么当下一次CPU将时间让给线程A进行执行时,才能正确的切换到A,并继续执行下去。所以任务从保存到再加载的过程就是一次上下文切换。

虽然上下文切换可以让我们觉得可以“同时”做很多事,但是上下文切换也是需要系统开销的。在《Java并发编程的艺术》中,作者举例演示了串行和并发执行累加操作,在结果中可以看得出,累加操作不同的次数会对不同的结果,所消耗的时间也有差别的。如果累加操作的次数没有超过百万次,那么串行执行结果消耗的时间会比并行执行的时间要少。所以在有些情况下我们需要尽可能的减少上下文切换的次数,使用的方法有:无锁并发编程,CAS算法,使用最少线程和使用协程。(这里笔者也只知道有这几种方法,至于具体如何使用以及在何种场景下使用还未深入研究)。

volatile与synchronized

volatile

volatile是轻量级的synchronized,它保证了在多处理器开发中,共享变量的可见性,并且volatile不会引起上下文切换和调度。可见性的意思是当一个线程修改了某个变量的值,另外一个线程可以读到这个变量修改后的值,如果一个变量被volatile修饰,那么Java内存模型确保所有线程看到这个变量的值是一致的。

synchronized

Java中每一个对象都可以作为锁,具体表现为:

  • 对于普通的同步方法,锁是当前实例对象
  • 对于静态的同步方法,锁是当前类的Class对象
  • 对于同步方法块,锁是synchronized括号里配置的对象

当一个线程访问同步代码块时,必须要先得到锁,退出或抛出异常时,必须释放锁。对于上述三种情况,表现形式为:

     /**
* 普通同步方法,锁是当前实例对象
*/
public synchronized void test1(){
//TODO something
} /**
* 静态同步方法,锁是当前类的Class对象
*/
public static synchronized void test2(){
//TODO something
} /**
* 同步方法块,锁是synchronized括号中的对象,这里是a
*/
public void test3(Integer a){
synchronized (a){
//TODO something
}
}

Java内存模型

Java中所有实例域、静态域和数组元素都存储在堆内存中,堆内存在线程之间共享。

Java线程之间的通信由Java内存模型(JMM)控制。JMM定义了线程和主内存的关系:线程之前的共享变量存储在主内存中,每个线程都有一个私有的本地内存(也叫工作内存),本地内存中存储了该线程读写共享变量的副本。本地内存是JMM的抽象概念,不真实存在,包涵了缓存,写缓冲区,寄存器以及其他硬件和编译器优化。Java内存模型结构图: 

从上图可以看出,线程A要与线程B进行通信的话,必须要经过两个步骤:

  1. 线程A把本地内存A中更新过的共享变量刷新要主内存中去,
  2. 线程B到主内存中获取更新之后的共享变量。

如下图:

重排序

重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

数据依赖性

定义:如果两个操作同时访问一个变量,且这两个操作中有一个为写操作。此时这两个操作之间就存在数据依赖性。

编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。

as-if-serial语义

语义:不管怎么重排序,单线程程序的执行结果不能被改变。编译器,runtime和处理器都必须遵守as-if-serial语义。

为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作进行重排序,但是如果操作之间不存在数据依赖关系,那么就有可能被进行重排序。例如:

 double pi = 3.14 ;  //A
double r = 1.0 ; //B
double area = pi * r *r ; //C

上面代码中,C依赖A,C依赖B,所以编译器不会重排序将C排在A,B之前。但是A,B之间没有依赖,所以可能被进行重排序,最终的执行顺序有两种:

A->B->C;

B->A->C;

这两种执行顺序对最终结果不会造成影响。

因为存在重排序,所以单线程程序不一定按照程序的顺序来执行。

该文主要讲述了一些偏概念的东西,先有一些印象,后续会以代码示例的形式进行全面的复习。

读《Java并发编程的艺术》(一)的更多相关文章

  1. 那些年读过的书《Java并发编程实战》和《Java并发编程的艺术》三、任务执行框架—Executor框架小结

    <Java并发编程实战>和<Java并发编程的艺术>           Executor框架小结 1.在线程中如何执行任务 (1)任务执行目标: 在正常负载情况下,服务器应用 ...

  2. 读《Java并发编程的艺术》学习笔记(一)

    接下来一个系列,是关于<Java并发编程的艺术>这本书的读书笔记以及相关知识点,主要是为了方便日后多次复习和防止忘记.废话不多说,直接步入主题: 第1章  并发编程的挑战 并发编程的目的是 ...

  3. Java并发编程的艺术读书笔记(2)-并发编程模型

    title: Java并发编程的艺术读书笔记(2)-并发编程模型 date: 2017-05-05 23:37:20 tags: ['多线程','并发'] categories: 读书笔记 --- 1 ...

  4. Java并发编程的艺术(六)——线程间的通信

    多条线程之间有时需要数据交互,下面介绍五种线程间数据交互的方式,他们的使用场景各有不同. 1. volatile.synchronized关键字 PS:关于volatile的详细介绍请移步至:Java ...

  5. Java并发编程的艺术(三)——volatile

    1. 并发编程的两个关键问题 并发是让多个线程同时执行,若线程之间是独立的,那并发实现起来很简单,各自执行各自的就行:但往往多条线程之间需要共享数据,此时在并发编程过程中就不可避免要考虑两个问题:通信 ...

  6. 《Java并发编程的艺术》留给自己以后看的笔记

    <Java并发编程的艺术>这本书特别好,和<深入了解JAVA虚拟机>有一拼,建议做java的都看看,下面全部都是复制书中的部分内容,主要目的是做个笔记,方便以后遇到问题能找到. ...

  7. 读书笔记之《Java 并发编程的艺术》

    一.多线程语义 即使是单核处理器也支持多线程执行代码,CPU 通过给每个线程分配 CPU 时间片来执行任务,当前任务执行一个时间片后会切换到下一个任务,所以 CPU 通过不停的切换线程执行. 并发执行 ...

  8. 《Java并发编程的艺术》读书笔记:二、Java并发机制的底层实现原理

    二.Java并发机制底层实现原理 这里是我的<Java并发编程的艺术>读书笔记的第二篇,对前文有兴趣的朋友可以去这里看第一篇:一.并发编程的目的与挑战 有兴趣讨论的朋友可以给我留言! 1. ...

  9. Java并发编程的艺术读书笔记(1)-并发编程的挑战

    title: Java并发编程的艺术读书笔记(1)-并发编程的挑战 date: 2017-05-03 23:28:45 tags: ['多线程','并发'] categories: 读书笔记 --- ...

随机推荐

  1. require include php5中最新区别,百度上好多错的。

    二者报错机制不同,include是warning 继续执行程序,require会报致命错误,中断程序运行. 前者有返回值,后者则没有. 注意了,php5里有一个区别没了,之前说的是require是无条 ...

  2. LeetCode -- Word Break 动态规划,详细理解

    Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separa ...

  3. 02.PHP7.x编译详解

    #php7编译安装安装 ``` useradd -M -s /sbin/nologin www yum -y install openssl-devel bzip2-devel curl-devel ...

  4. echarts雷达图点击事件

    最近看见别人问的问题,点击雷达图的拐点,获取点击数据的问题,直接上代码. echarts配置问题:https://www.douban.com/note/509404582/ <!doctype ...

  5. ZEROC ICE 跨平台间程序调用 java版

    前言: 本来建博客是为了和大家分享一些前端的开发经验和心得呢,但是阴差阳错,第一篇技术博客确实关于跨平台,跨语言服务端调用的解决方案---ZEROC ICE. 最近一个项目涉及到java.python ...

  6. Andorid自动读取短信验证码

    手机收到验证码短信后,程序自动识别验证码并填充验证码输入框. 思路是有了,实现的方式也有多种: 1.开启一个线程,隔一段时间就去查询收件箱是否有变化,有变化再读取出来做处理. 2.注册一个短信变化的广 ...

  7. ios UICollectionView简单说明

    原谅我记不住写下来好了 UICollectionViewFlowLayout 流式自动布局 继承于UICollectionViewLayout 初始化:[[UICollectionViewFlowLa ...

  8. [Oracle]审计Audit

    1.Audit的概念 Audit是监视和记录用户对数据库进行的操作,以供DBA进行问题分析.利用Audit功能,可以完成以下任务: 监视和收集特定数据库活动的数据.例如管理员能够审计哪些表被更新,在某 ...

  9. 第六章 Hibernate关联映射

    第六章 hibernate关联映射一.本章知识点分为2部分:1.关联关系:单向多对一关联关系,双向一对多关联关系(含一对多关联关系),多对多关联关系2.延迟加载:类级别加载策略,一对多加载策略,多对一 ...

  10. linux 程序调用system执行命令

    正确使用system方法,判断返回值 int exeCmd(const char *cmd) { pid_t status; status = system(cmd); == status) { Wr ...