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

最近在进一步学习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. Python全栈开发第13天

    #多用户登录 import getpass #引用getpass import os #引用os import configparser #引用配置文件操作的库 count = 0 count_oth ...

  2. 分布式文件管理系统_FastDFS集群

    简单介绍 1,client storage tracker的关系 先用一幅图来解释用户如何访问一个通过DFS管理的文件 一般来说,一台服务器只有一个storage server,多个storage s ...

  3. 转载linux c语言程序的Makefile编写

    对于程序设计员来说,makefile是我们绕不过去的一个坎.可能对于习惯Visual C++的用户来说,是否会编写makefile无所谓.毕竟工具本身已经帮我们做好了全部的编译流程.但是在Linux上 ...

  4. jmeter 使用jmeter 录制web脚本

    1.打开jmeter.鼠标右击工作台.添加HTTP代理服务器 2.设置端口号.目标控制器.分组 3.添加查看结果树 4.点击启动.确定完成 5.打开浏览器直接进行操作.就可以看到所录制的脚本信息

  5. c#遍历文件夹获得所有文件

    在c#中,想要获得一个文件夹下的所有子目录以及文件十分简单. 首先,获取目录的情况下,DirectoryInfo.GetDirectories():获取目录(不包含子目录)的子目录,返回类型为Dire ...

  6. 跟着刚哥梳理java知识点——运算符(五)

    运算符:是一种特殊的符号,用以表示数据的运算.赋值和比较. 1.算数运算符(+.-.*./.%.++.--) a)除: int i = 12; double d1 = i / 5; //2.0 dou ...

  7. C#,VB.NET 如何将Excel转换为Text

    在工作中,有时我们需要转换文档的格式,之前已经跟大家介绍过了如何将Excel转换为PDF.今天将与大家分享如何将Excel转换为Text.这次我使用的依然是免费版的Spire.XLS for .NET ...

  8. servlet:从入门到实战学习(1)---全·环境配置

    最近公司忙加班学习,学校忙助教工作,博客鸽了好久,后端学习工作过程中学了好多东西,趁着工作之余得空补补博客,算是整理下学习的东西. javaweb的后端研发需要学习的是tomcat+servlet+j ...

  9. java类集框架(ArrayList,LinkedList,Vector区别)

    主要分两个接口:collection和Map 主要分三类:集合(set).列表(List).映射(Map)1.集合:没有重复对象,没有特定排序方式2.列表:对象按索引位置排序,可以有重复对象3.映射: ...

  10. hyper-v使用wifi链接网络

    公司了给本屌一个thinkpad笔记本,10G内存.想不出拿来干什么...装了一个win8.1_64位,cf,qq,hyper-v. 昨天第一次玩hyper-v新建了的时候选择“第二代”坑爹就开始了, ...