Java内存模型( JMM ) :

1) 所有的变量都存储在主内存中

2) 每个线程都有自己独立的工作内存, 里面保存该线程使用到的变量的副本 ( 主内存中该变量的一份拷贝 )

JMM两条规定:

1) 线程对共享变量的所有操作都必须在自己的工作内存中进行

2) 不同线程之间无法直接访问其他线程工作内存中的共享变量, 线程间共享变量值的传递必须通过主内存

线程间共享变量可见性实现的原理:

线程A 对共享变量的修改想被线程B 及时看到, 必须要经过以下2个步骤:

1) 把线程A 工作内存中更新过的共享变量刷新到主内存中( store )

2) 将主内存中最新的共享变量的值共享到线程B 工作内存中( load )

Java 语言层面支持的可见性实现方式:

1) synchronized

2) volatile

JUC 包下的类也可以实现可见性

1) Atomic

2) ReentrantLock

3) Semaphore

1. synchronized 实现可见性

JMM 关于 synchronized 的两条规定:

1) 线程释放锁前, 必须把共享变量的最新值从该线程的工作内存刷新到主内存中

2) 线程持有锁时, 将清空该线程工作内存中共享变量的值, 从主内存中读取最新的值

synchronized 实现可见性的原因:

线程释放锁前对共享变量的修改在下次持有锁时对其他线程可见

public class SynchronizedDemo {

// 共享变量

private int result = 0;

// 共享变量执行自增操作

public synchronized void increase() {

result++;

}

public int getResult() {

return result;

}

public static void main(String[] args) throws InterruptedException {

final SynchronizedDemo demo = new SynchronizedDemo();

// 设置启动500个线程

int count = 500;

// 创建一个 JUC 包下的同步同步计数器, 设置计数次数为线程数500

final CountDownLatch cdl = new CountDownLatch(count);

for (int i = 0; i

new Thread(new Runnable() {

@Override

public void run() {

demo.increase();

cdl.countDown();

}

}).start();

}

cdl.await();

System.out.println(demo.getResult());

}

}

Console 输出: 500

synchronized 不仅可以实现可见性, 还可以实现原子性

volatile 如何实现可见性:

通过加入内存屏障和禁止指令重排序

1) 对 volatile 变量执行写操作时, 会在写操作后加入一条 store 屏障指令

2) 对 volatile 变量执行读操作时, 会在读操作前加入一条 load 屏障指令

public class VolatileDemo {

// 使用 volatile 修饰共享变量

private volatile int result = 0;

// 共享变量 result 执行自增操作, 无法保证原子性

public void increase() {

result++;

}

public int getResult() {

return result;

}

public static void main(String[] args) throws InterruptedException {

final VolatileDemo demo = new VolatileDemo();

// 设置启动500个线程

int count = 500;

// 创建一个 JUC 包下的同步同步计数器, 设置计数次数为线程数500

final CountDownLatch cdl = new CountDownLatch(count);

for (int i = 0; i

new Thread(new Runnable() {

@Override

public void run() {

demo.increase();

cdl.countDown();

}

}).start();

}

cdl.await();

System.out.println(demo.getResult());

}

}

Console 输出: 498

volatile 关键字, 能保证 volatile 变量的可见性, 不能保证 volatile 变量操作的原子性( 如 ++/-- )

3. AtomicInteger 实现可见性

用 Atomic 类实现共享变量在线程中的原子性( 通过 CAS, 自旋 实现)

public class AtomicIntegerDemo {

// 共享变量

private AtomicInteger result = new AtomicInteger(0);

// 使用 Atomic 类的 incrementAndGet() 方法( 原子操作 ), 实现自增

public void increase() {

result.incrementAndGet();

}

public int getResult() {

return result.get();

}

public static void main(String[] args) throws InterruptedException {

final AtomicIntegerDemo demo = new AtomicIntegerDemo();

// 设置启动500个线程

int count = 500;

// 创建一个JUC包下的同步同步计数器, 设置计数次数为线程数500

final CountDownLatch cdl = new CountDownLatch(count);

for (int i = 0; i

new Thread(new Runnable() {

@Override

public void run() {

demo.increase();

cdl.countDown();

}

}).start();

}

cdl.await();

System.out.println(demo.getResult());

}

}

Console 输出: 500

4. JUC 包下的 Lock 实现可见性

用 ReentrantLock 实现共享变量在线程中的原子性

public class LockDemo {

// 共享变量

private int result = 0;

// 可重入锁

private Lock lock = new ReentrantLock();

// 使用锁机制, 保证锁内代码的原子性

public void increase() {

// 加锁

lock.lock();

try {

result++;

} finally {

// 释放锁

lock.unlock();

}

}

public int getResult() {

return result;

}

public static void main(String[] args) throws InterruptedException {

final LockDemo demo = new LockDemo();

// 设置启动500个线程

int count = 500;

// 创建一个JUC包下的同步同步计数器, 设置计数次数为线程数500

final CountDownLatch cdl = new CountDownLatch(count);

for (int i = 0; i

new Thread(new Runnable() {

@Override

public void run() {

demo.increase();

cdl.countDown();

}

}).start();

}

cdl.await();

System.out.println(demo.getResult());

}

}

Console 输出: 500

5. Semaphore 实现可见性

用信号量机制实现共享变量在线程中的原子性

public class SemaphoreDemo {

// 共享变量

private int result = 0;

// 初始化信号量为1, 一次只能有1个线程访问共享变量, 相当于互斥锁

private Semaphore semaphore = new Semaphore(1);

// 使用信号量机制, 保证共享变量自增操作的原子性

public void increase() {

try {

// 获取1个信号量

semaphore.acquire();

} catch (InterruptedException e) {

e.printStackTrace();

}

result++;

// 释放1个信号量

semaphore.release();

}

public int getResult() {

return result;

}

public static void main(String[] args) throws InterruptedException {

final SemaphoreDemo demo = new SemaphoreDemo();

// 设置启动500个线程

int count = 500;

// 创建一个JUC包下的同步同步计数器, 设置计数次数为线程数500

final CountDownLatch cdl = new CountDownLatch(count);

for (int i = 0; i

new Thread(new Runnable() {

@Override

public void run() {

demo.increase();

cdl.countDown();

}

}).start();

}

cdl.await();

System.out.println(demo.getResult());

}

}

Console 输出: 500

总结:

synchronized 代码块具备 可见性和 可见性

volatile变量具备可见性, 不具备原子性

java 线程可见性,Java多线程之内存可见性相关推荐

  1. 什么是java线程?java线程模型的组成

    关于java线程的文章早已是非常多了,本文是对我个人过往学习java,理解及应用java线程的一个总结.此文内容涉及java线程的基本概念,以及什么是java线程等相关问题,希望对大家有所帮助. 什么 ...

  2. qotd服务_QOTD:Java线程与Java堆空间

    qotd服务 以下问题很常见,并且与OutOfMemoryError有关:在JVM线程创建过程和JVM线程容量期间无法创建新的本机线程问题. 这也是我向新技术候选人(高级职位)提出的典型面试问题. 我 ...

  3. JavaSE学习53:细说多线程之内存可见性

    一共享变量在线程间的可见性 (1)有关可见性的一些概念介绍 可见性:一个线程对共享变量值的修改,能够及实地被其他线程看到. 共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几 ...

  4. java线程钥匙_Java多线程并发编程/锁的理解

    一.前言 最近项目遇到多线程并发的情景(并发抢单&恢复库存并行),代码在正常情况下运行没有什么问题,在高并发压测下会出现:库存超发/总库存与sku库存对不上等各种问题. 在运用了 限流/加锁等 ...

  5. java线程池_Java多线程并发:线程基本方法+线程池原理+阻塞队列原理技术分享...

    线程基本方法有哪些? 线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等. 线程等待(wait) 调用该方法的线程进入 WAITING 状态,只有等 ...

  6. java 线程开销_多线程的线程开销

    多线程中两个必要的开销:线程的创建.上下文切换 创建线程: 创建线程使用是直接向系统申请资源的,对操作系统来说,创建一个线程的代价是十分昂贵的, 需要给它分配内存.列入调度,同时在线程切换的时候还要执 ...

  7. java线程入门_java多线程快速入门(一)

    1.什么是进程 比如:QQ.QQ游戏.eclipse都是进程,可以通过任务管理器查看进程 2.进程和线程区别 线程是进程的一部分,一个进程可以包含多个线程,一个线程只能属于一个进程 进程是所有线程的集 ...

  8. java线程 锁_Java多线程(二) 多线程的锁机制

    当两条线程同时访问一个类的时候,可能会带来一些问题.并发线程重入可能会带来内存泄漏.程序不可控等等.不管是线程间的通讯还是线程共享数据都需要使用Java的锁机制控制并发代码产生的问题.本篇总结主要著名 ...

  9. java线程设计模式_JAVA多线程设计模式

    漫谈UML UML 类图 类和层次结构的关系 接口与实现 聚合 访问控制 类间的关联性 顺序图 处理流程和对象间的协调 时序图 Introduction 1 Java语言的线程 Java语言的线程 何 ...

  10. java 线程分组_Java多线程可以分组,还能这样玩!

    前面的文章,栈长和大家分享过多线程创建的3种方式<实现 Java 多线程的 3 种方式>. 但如果线程很多的情况下,你知道如何对它们进行分组吗? 和 Dubbo 的服务分组一样,Java ...

最新文章

  1. 写给小白看的线程和进程,高手勿入
  2. python3示例_Python3 实例(七)
  3. plsql视图添加表字段_教你不到两分钟,快速创建数据透视图
  4. 安卓分辨率_安卓界面的尺寸规范有哪些?
  5. ICCV 2021 揭榜!十大方向抢先看!(Transformer/分割/Action/插帧/超分等)
  6. DEDE友情链接修改
  7. JPack插件停止更新,希望玩wow的朋友可以继续开发这个插件
  8. 把ts自动合并 下载网页视频并自动合成视频
  9. jeesite应用实战(数据增删改查),认真读完后10分钟就能开发一个模块
  10. MySQL入门推荐书籍
  11. 人工智能知识全面讲解:最简单的神经元模型
  12. 抖音直播睡觉一晚赚7.6万,心态崩了
  13. 清除tomcat缓存HTML,清除Tomcat缓存
  14. 计算机网络为什么要分层,从形而上到形而下视角的理解
  15. MYIP网站信息状态条 v1.0.1.3 (MyIP Status Bar for IE)
  16. 教你用Python自制拼图小游戏,一起来玩吧
  17. 虚拟机登录MySQL
  18. OWC生成统计报表(柱形图)
  19. 元宇宙中的手势交互(四)第一款主流VR头显中的手势交互原理剖析(Meta Quest 2)
  20. 2019级C语言大作业 - 泡泡龙

热门文章

  1. linux yum及yum仓库
  2. 编程方法学15:指针要点回顾
  3. Tensorflow 错误总结:ImportError: cannot import name add_newdocs.
  4. 常见的几种最优化方法
  5. [云炬创业基础笔记] 第四章测试5
  6. C#中线程池的简单应用
  7. 提高C程序效率的10种方法
  8. Grub4Dos 安装Ubuntu 收藏
  9. go语言中goroutine池
  10. C语言必知必会-strtok赞歌