原子性实现那么我们该如何实现多线程的原子性:
- 使用synchronized(同步关键字)
synchronized( 对象 ) { // 要作为原子操作代码}
我们如果要实现之前的代码 , 我们可以将代码修改为:package cn.itcast.jvm.t4.avo;public class Demo4_1 {// 这里的i应该被多线程共用 , 设为静态变量static int i = 0;// 这里是Obj对象 , 我们设置它为锁 , 注意两个线程中的synchronized所对应的锁应该是同一个对象(锁)static Object obj = new Object();public static void main(String[] args) throws InterruptedException {// 采用synchronized设置锁实现原子性 , 这样i++操作就会完整进行Thread t1 = new Thread(() -> {synchronized (obj) {for (int j = 0; j < 50000; j++) {i++;}}});Thread t2 = new Thread(() -> {// 采用synchronized设置锁实现原子性 , 这样i--操作就会完整进行synchronized (obj) {for (int j = 0; j < 50000; j++) {i--;}}});t1.start();t2.start();t1.join();t2.join();// 我们的输出结果自然是0了~System.out.println(i);}}
内存模型之可见性我们将在下面仔细介绍可见性的特点可见性介绍首先我们简单介绍一下可见性的定义:
- 我们需要保证 , 在多个线程中 , 对同一变量的修改需要被其他线程所知道并且可以调用
- 我们的程序往往具有自动优化 , 对于多次取同一值的数据可能会封装在自己的程序中而不是在源程序读取 , 这就会导致可见性失效
package cn.itcast.jvm.t4.avo;public class Demo4_2 {static boolean run = true;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while(run){}});t.start();Thread.sleep(1000);run = false; // 线程t不会如预想的停下来}}
我们的运行结果如下:// 我们上述代码希望:程序在执行1s后停止运行 , 但我们的程序却一直运行不会停止...
可见性分析首先我们回顾开头的注意点:- 程序具有自身很多的优化步骤 , 可能哪一步就会导致我们的程序出错
- 初始状态 , t 线程刚开始从主内存读取了 run 的值到工作内存 。

文章插图
- 线程要频繁从主内存中读取 run 的值 , JIT 编译器会将 run 的值缓存至自己工作内存中的高速缓存中 , 减少对主存中 run 的访问

文章插图
- 1 秒之后 , main 线程修改了 run 的值 , 并同步至主存 , 而 t 是从自己工作内存中的高速缓存中读取这个变量的值 , 结果永远是旧值

文章插图
可见性实现我们的可见性经常通过一种修饰词来实现:
- volatile(易变关键字)
- 它可以用来修饰成员变量和静态成员变量
- 他可以避免线程从自己的工作缓存中查找变量的值 , 必须到主存中获取它的值 , 线程操作 volatile 变量都是直接操作主存
- synchronized 语句块
- synchronized既可以保证代码块的原子性 , 也同时保证代码块内变量的可见性
- 但缺点是synchronized是属于重量级操作 , 性能相对更低
经验总结扩展阅读
- Linux学习环境搭建流程
- Archlinux + Dwm 配置流程
- 关于入门深度学习mnist数据集前向计算的记录
- 四十 Java开发学习----MyBatisPlus入门案例与简介
- jvm双亲委派机制详解
- FHE学习笔记 #2 多项式环
- django-environ学习
- 万字详解JVM,让你一文吃透
- 论文笔记 - GRAD-MATCH: A Gradient Matching Based Data Subset Selection For Efficient Learning
- MFC 学习笔记