🦊 Java
1、Java基础
1.1 StringBuffer和StringBuilder的区别
1.2 ConcurrentHashMap和TreeMap实现原理
1.3 ArrayList和LinkedList实现原理
1.4 HashSet和TreeSet实现原理
1.5 深拷贝与浅拷贝
1.6 抽象类与接口
2、Java多线程
2.1 并发编程的三大特性
2.2 指令重排
2.3 Volatile原理
2.4 CAS原理
2.5 Java的4种引用级别
2.6 Java中的锁
2.7 Synchronized实现原理
2.8 线程池实现原理
2.9 AQS
2.10 创建线程的方式
2.11 ThreadLocal原理
3、JVM
3.1 判断对象是否存活的方法
3.2 JVM内存结构
3.3 常见的垃圾收集算法有哪些
3.4 指针碰撞和空闲列表
3.5 常见的垃圾收集器有哪些
3.6 内存溢出与内存泄漏的区别
3.7 常用的JVM启动参数有哪些
3.8 反射机制
4、NIO
4.1 概述
5、Spring
5.1 Spring IOC
5.2 Spring AOP
6、SpringBoot
6.1 SpringBoot、SpringCloud的联系与区别
-
+
游客
注册
登录
并发编程的三大特性
并发编程主要包含三大特性,分别是 `原子性`、`可见性` 和 `有序性`。 ## 1 原子性 ### 1.1 概念 原子性是指**在一次或者多次操作时,要么所有操作都被执行,要么所有操作都不执行**。 ### 1.2 示例 ```txt i = 0; //1 j = i ; //2 i++; //3 i = j + 1; //4 ``` 上面的四个操作中,只有 1 是原子操作,其余绝不是: 1. 1 在 `Java` 中,对**基本数据类型**的变量的**赋值**操作都是原子性。 2. 2 中包含了两个操作: 1. **读取 `i`。** 2. **将 `i` 结果赋值给 `j`。** 3. 3 中包含了三个操作: 1. **读取 `i`。** 2. **`i+1`。** 3. **将加 1 结果赋值给 `i`。** 4. 4 中同 3 一样。 ### 1.3 保证原子性的方法 1. 如果要保证**多个操作**的原子性,需要使用 `synchronized` 关键字或者 `lock` 相关的工具类。 2. 如果要使 `int`、`long` 等类型的自增操作具有原子性,可以使用 `java.util.concurrent.atomic` 包下的工具类,如:`AtomInteger`、`AtomLong` 等。 3. 需要注意的是,`volatile` 关键字**不具有保证原子性的语义**。 ## 2 可见性 ### 2.1 概念 可见性是指**当一个线程对共享变量进行修改后,另外一个线程可以立即看到该变量修改后的最新值**。 ### 2.2 示例 ```java /** * @author peng.wei * @version 1.0 * @date 2021/4/19 16:12 * @Description JUC 可见性测试 */ public class VisibilityTest { public static int count = 0; public static void main(String[] args) { final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); //读取 count 值的线程 new Thread(() -> { System.out.println("开始读取 count..."); int i = count;//存放 count 的更新前的值 while (count < 3) { if (count != i) {//当 count 的值发生改变时,打印 count 被更新 System.out.println(sdf.format(new Date()) + " count 被更新为" + count); i = count;//存放 count 的更新前的值 } } }).start(); //更新 count 值的线程 new Thread(() -> { for (int i = 1; i <= 3; i++) { //每隔 1 秒为 count 赋值一次新的值 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(sdf.format(new Date()) + " 赋值 count 为" + i); count = i; } }).start(); } } ``` 输出结果如下: ```txt 开始读取 count... 16:14:33.186 赋值 count 为 1 16:14:34.190 赋值 count 为 2 16:14:35.194 赋值 count 为 3 ``` 从运行的输出结果可以看出,读取 `count` 值的线程一直没有读取到 `count` 的最新值,这是因为在读取 `count` 值的线程中,第一次是从**主内存**中读取 `count` 值后写入**工作内存**,再从工作内存中读取,之后都是从自己的工作内存中读取的 `count` 值,因此并**没有发现更新 `count` 值的线程对 `count` 值的修改**。 ### 2.3 保证可见性的方法 在 `Java` 中可以用以下 3 种方式保证可见性: 1. **使用 `volatile` 关键字:** 当一个变量被 `volatile` 关键字修饰时,**其他线程对该变量进行了修改后,会导致当前线程在工作内存中的变量副本失效,必须从主内存中再次获取,当前线程修改工作内存中的变量后,同时也会将其修改刷新到主内存中**。 2. **使用 `synchronized` 关键字:**`synchronized` 关键字能够保证**同一时刻只有一个线程获得锁**,然后**执行同步方法或者代码块**,并且确保**在锁释放之前,把变量的修改刷新到主内存中**。 3. **使用 `Lock` 相关的工具类:**`Lock` 相关的工具类的 `lock` 方法能够保证**同一时刻只有一个线程获得锁**,然后**执行同步代码块**,必须确保**执行 `unlock` 方法之前,把变量的修改刷新到主内存中**。 ## 3 有序性 ### 3.1 概念 有序性指的是**程序执行的顺序按照代码的先后顺序执行**。 ### 3.2 示例 ```java package top.grayson.juc.characteristic; /** * @author peng.wei * @version 1.0 * @date 2021/4/19 16:34 * @Description JUC 有序性测试 */ public class Singleton { private Singleton (){} private static boolean isInit = false; private static Singleton instance; public static Singleton getInstance() { if (!isInit) {//判断是否初始化过 instance = new Singleton();//初始化 isInit = true;//初始化标识赋值为 true } return instance; } } ``` 1. 这是一个有问题的单例模式示例。 2. 假如在编译期或运行期时指令重排,把 `isInit = true;` 重新排序到 `instance = new Singleton();` 的前面,**在单线程运行时,程序重排后的执行结果和代码顺序的执行的结果是完全一样的**,但是在多线程运行时就极有可能出现问题。 3. 比如,一个线程先判断 `isInit` 为 `false` 进行初始化,本应在初始化后再把 `isInit` 赋值为 `true`,但是因为指令重排后没初始化就把 `isInit` 赋值为 `true`,恰好此时另外一个线程在判断是否初始化过,`isInit` 为 `true` 就执行返回了 `instance`,这是一个没有初始化的 `instance`,肯定造成不可预知的错误。 ### 3.3 如何保证有序性 1. 使用 `volatile` 关键字保证有序性。 2. 使用 `synchronized` 关键字保证有序性。 3. 使用 `Lock` 相关的工具类保证有序性。 ## 4 参考文献 1. [学妹教你并发编程的三大特性:原子性、可见性、有序性](https://www.cnblogs.com/heihaozi/p/12909955.html)。
ricear
2022年7月11日 21:59
©
BY-NC-ND(4.0)
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码