前段时间一直在学习多线程相关的知识,目前也算有了一个整体的认识,今天呢,主要从整体介绍一下,只谈造火箭,拧螺丝这种细节还需要自己深究。

首先是操作系统级别对于多线程的支持,由 CPU 的多级缓存、缓存一致性、乱序执行优化等问题而设计出 Java 内存模型。关于这部分我前面已经总结过。

彻底搞懂 CPU 中的内存结构

Java 内存模型 ,一篇就够了!

说完了操作系统级别的多线程的后备知识以及 Java 内存模型的设计,接着说说 多线程的实现以及Java 中的多线程是怎么实现的,具体可以看这篇。

多线程实现原理

上面说的都是一些原理,深层次的东西,你若是不懂,也不影响你使用 Java 多线程来编码,无非就是继承 Thread 实现 Runnable 。但是呢,使用多线程,不可避免的就会出现线程安全问题。

虽说我们也知道,不管三七二十一我使用 synchronized 关键字就好使,但是呢,想要知其所以然的同学还是需要好好学习一下上面说的那些原理的。而且,处理线程安全问题会涉及到性能问题,这就是高手之间的差别了。

这么一说,我们的重点就放在如何保证线程安全的问题上了,首先,线程安全的特性有 3 个,原子性,可见性,有序性。我们主要看看 Java 中都是使用什么方式来保证这 3 个特性的。

主要的体现在 atomic 包、CAS 思想、synchronized 锁、Lock 锁、volatile 关键字、还有一个非常重要的 JUC 包,别急,后面一个一个的说。

哎,发现我已经说了 atomic 和 CAS 了,atomic包、synchronized | Java 中线程安全 关于 volatile 关键字,在 JVM 中的语义,即为防止指令重排并添加内存屏障,简单来说就是限制指令的执行顺序,并默认添加一些指令(内存屏障)以达到有序和可见性的目的。比方说在 volatile 变量读操作之前必须先完成写。

简单提一下,使用 volatile 修饰的变量不一定是线程安全的,除非对变量的操作都是原子性的。

在说 JUC 之前,先说一种比较讨巧的手段来处理多线程问题,我们知道多线程中存在问题主要就是在不同线程之间对共享变量进行操作,使得数据变脏。那我们在多线程中杜绝出现共享变量就行了呀,基于此,也就出现了 ThreadLocal 这个类。

ThreadLocal 也被称为线程局部变量,实际上它是封装了一个当前线程为 K 的一个 Map,将需要用到的变量存进 Map,用的时候取出来,而这个 Map 又是线程私有的,其它线程无法访问,自然是线程安全的。

使用 ThreadLocal 有个实际的例子,我们在过滤器中添加变量到 ThreadLocal 中,在拦截器中删除 ThreadLocal 中的值,这个变量可能是用户唯一性的标识或是其它,因为每一个请求都会触发一个线程执行,经过过滤器之后设置好变量,在方法中使用,而在拦截器中对请求处理之后即可对 ThreadLocal 中的数据进行清除,不然会发生内存泄漏。

为什么 ThreadLocal 可以保证线程安全,因为它存放的变量都是线程私有的,还有一种我们经常定义的变量也是线程私有的,那就是我们定义的局部变量,方法内的变量。因为这些变量是存放在线程栈上面,这部分区域是线程私有的,也就是我们之前说的工作内存的概念,所以我们经常写局部变量,而从来没有考虑过线程安全问题,实际原因在这。

线程私有可以保证线程安全,但是全局变量又是少不了一环,偶尔定义了那么一个,一不留神,在多线程环境中可能就会出错。最最常说的无非就是那几个集合类和 String 相关的类,当然还有其它的不安全的类,可能你已经使用了但还不知道。

既然知道不安全了,那在多线程的环境中就避免使用它,而贴心的 JDK 也提供了一套与之对应的安全的类给我们使用,我们要做的就是知道哪些类是不安全的,对应的安全的类是什么以及如何保证的安全。

先说 String、StringBuffer、StringBuilder 之间细微的差别,String 是不可变类,不可变类本身就是定义的时候就是线程安全的,关于不可变类这又是一块知识,暂时不说了。

但是也正是因为 String 的不可变,所以每次对字符串的修改都会新创建一个字符串,而最终我们想要得到的可能就是最后的一个值,中间那些中间值太占空间。嗯,优化之后就有了后面两兄弟。

简单说 StringBuffer 是线程安全的,而 StringBuilder 是不安全的,这两个较 String 都是可变对象。所以说,在单线程中使用 StringBuilder 代替 String ,多线程中使用 StringBuffer 代替 String 是个不错的选择。对了,StringBuffer 保证线程安全的手段也就是使用 synchronized 方法。

还有就是关于集合类相关的线程安全操作,有一个 Collections 工具类,提供了一套 synchronizedXXX 方法,好吧,都写在脸上了,这样一来,List、Set、Map 都摇身一变安全了。

还有一些古老的实现类,像什么 Vector,Hashtable,也是可以实现线程安全的,但是一来是基本不使用,二是有些情况下可能并不能保证线程安全,像 Vector 中的  add 和 remove 方法就是没有进行同步操作,在某些情况下就会出现不安全。

不知道你们有没有发现,这些古老的实现类中实现安全的方式无非就是添加 synchronized 关键字。有没有高级一点的操作啊,肯定有,但是今天扯不动了,下次再说吧!

简单总结一下,今天说了什么,回顾之前的文章并理顺了整体的思路,从底层原理到 JMM,从多线程的实现到如何保证线程安全,又举例说明了 JDK 中类的设计思路,后面的 JUC 设计的更秀。

说的挺多,但是万变不离其宗,做好分类,学习记忆更便捷,Java 保证线程安全底层看 3 大特性,语言实现层面主要看锁,锁也就两大类 synchronized 和 Lock,只不过分支极多而已,还有一层是算法层面,像什么 CAS 。但是,回到 CPU 时代还他么都是 0、1 代码,所以,不要怂就是干!

Java 并发编程整体介绍 | 内含超多干货的更多相关文章

  1. [Java并发编程(三)] Java volatile 关键字介绍

    [Java并发编程(三)] Java volatile 关键字介绍 摘要 Java volatile 关键字是用来标记 Java 变量,并表示变量 "存储于主内存中" .更准确的说 ...

  2. [Java 并发] Java并发编程实践 思维导图 - 第一章 简单介绍

    阅读<Java并发编程实践>一书后整理的思维导图.

  3. Java并发编程(三):并发模拟(工具和Java代码介绍)

    并发模拟工具介绍 ① Postman : Http请求模拟工具 从图上我们可以看出,Postman模拟并发其实是分两步进行操作的.第一步:左边的窗口,在窗口中设置相关接口以及参数,点击运行进行第二步. ...

  4. 如何评价《Java 并发编程艺术》这本书?

    对于书评这件事情,我其实是不想写的,因为每个人都有自己的一个衡量标准,每个人眼中都有自己的哈姆雷特,是好是坏每个人都褒贬不一.如果对于书中的知识你都掌握了,你只是想把它作为一种知识串联的记忆体的话,那 ...

  5. Java并发编程深入学习

    上周的面试中,被问及了几个并发开发的问题,自己回答的都不是很系统和全面,可以说是"头皮发麻",哈哈.因此果断购入<Java并发编程的艺术>一书,该书内容主要是对ifev ...

  6. Java并发编程——BlockingQueue

    简介 BlockingQueue很好的解决了多线程中,如何高效安全"传输"数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利. 阻塞队列是 ...

  7. Java并发编程基础之volatile

    首先简单介绍一下volatile的应用,volatile作为Java多线程中轻量级的同步措施,保证了多线程环境中“共享变量”的可见性.这里的可见性简单而言可以理解为当一个线程修改了一个共享变量的时候, ...

  8. Java并发编程75道面试题及答案

    1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...

  9. 《java并发编程实战》笔记

    <java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为:  Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...

随机推荐

  1. d 属性: 赋予字段执行动作的能力

    1.对只读属性误解 property AppSetting: ISuperobject read fAppSetting;当看到 AppInfo.AppSetting.D['lastLat'] := ...

  2. Mask RCNN 学习笔记

    下面会介绍基于ResNet50的Mask RCNN网络,其中会涉及到RPN.FPN.ROIAlign以及分类.回归使用的损失函数等 介绍时所采用的MaskRCNN源码(python版本)来源于GitH ...

  3. SpringBoot使用外置的Servlet容器

    SpringBoot默认使用嵌入式的Servlet容器,应用打包成可执行的jar包 优点:简单.便携 缺点:默认不支持jsp,优化定制比较复杂(使用定制器serverProperties.自定义Emb ...

  4. ARMV8 datasheet学习笔记4:AArch64系统级体系结构之编程模型(2)- 寄存器

    1. 前言 2. 指令运行与异常处理寄存器 ARM体系结构的寄存器分为两类: (1)系统控制和状态报告寄存器 (2)指令处理寄存器,如累加.异常处理 本部分将主要介绍如上第(2)部分的寄存器,分为AA ...

  5. 利用capability特征加强Linux系统安全【转】

    转自:https://blog.csdn.net/fivedragon/article/details/676849 1.简介 UNIX是一种安全操作系统,它给普通用户尽可能低的权限,而把全部的系统权 ...

  6. jvm系列一、java类的加载机制

    一.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构 ...

  7. shell编程之helloworld

    /bin/sh与/bin/bash的区别sh:如果前面有语句报错,则报错语句后面的命令不执行bash:如果前面有语句报错,后面的命令也会执行sh跟bash的区别,实际上就是bash有没有开启posix ...

  8. KVM -> 虚拟机管理&console登录_02

    1.KVM虚拟机管理操作 virsh命令常用参数总结 1.开机关机: virsh list (只可以查看运行的虚拟机) virsh list  --all (全部都可以查看) 开机与关机: virsh ...

  9. 解决walle报错:宿主机代码检出检测出错,请确认svn用户名密码无误

    使用walle检测报错: 查看日志 # tail -f /tmp/walle/walle-20161010.log 报错: 2016-10-10 14:20:30 -- --------------- ...

  10. centos常用网络管理命令

    网卡配置命令:ifconfig (ip addr , ip link) ifconfig:显示所有活动状态的相关信息    ifconfig Interface:仅显示指定接口的相关信息    ifc ...