java 线程可见性,Java多线程之内存可见性
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多线程之内存可见性相关推荐
- 什么是java线程?java线程模型的组成
关于java线程的文章早已是非常多了,本文是对我个人过往学习java,理解及应用java线程的一个总结.此文内容涉及java线程的基本概念,以及什么是java线程等相关问题,希望对大家有所帮助. 什么 ...
- qotd服务_QOTD:Java线程与Java堆空间
qotd服务 以下问题很常见,并且与OutOfMemoryError有关:在JVM线程创建过程和JVM线程容量期间无法创建新的本机线程问题. 这也是我向新技术候选人(高级职位)提出的典型面试问题. 我 ...
- JavaSE学习53:细说多线程之内存可见性
一共享变量在线程间的可见性 (1)有关可见性的一些概念介绍 可见性:一个线程对共享变量值的修改,能够及实地被其他线程看到. 共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几 ...
- java线程钥匙_Java多线程并发编程/锁的理解
一.前言 最近项目遇到多线程并发的情景(并发抢单&恢复库存并行),代码在正常情况下运行没有什么问题,在高并发压测下会出现:库存超发/总库存与sku库存对不上等各种问题. 在运用了 限流/加锁等 ...
- java线程池_Java多线程并发:线程基本方法+线程池原理+阻塞队列原理技术分享...
线程基本方法有哪些? 线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等. 线程等待(wait) 调用该方法的线程进入 WAITING 状态,只有等 ...
- java 线程开销_多线程的线程开销
多线程中两个必要的开销:线程的创建.上下文切换 创建线程: 创建线程使用是直接向系统申请资源的,对操作系统来说,创建一个线程的代价是十分昂贵的, 需要给它分配内存.列入调度,同时在线程切换的时候还要执 ...
- java线程入门_java多线程快速入门(一)
1.什么是进程 比如:QQ.QQ游戏.eclipse都是进程,可以通过任务管理器查看进程 2.进程和线程区别 线程是进程的一部分,一个进程可以包含多个线程,一个线程只能属于一个进程 进程是所有线程的集 ...
- java线程 锁_Java多线程(二) 多线程的锁机制
当两条线程同时访问一个类的时候,可能会带来一些问题.并发线程重入可能会带来内存泄漏.程序不可控等等.不管是线程间的通讯还是线程共享数据都需要使用Java的锁机制控制并发代码产生的问题.本篇总结主要著名 ...
- java线程设计模式_JAVA多线程设计模式
漫谈UML UML 类图 类和层次结构的关系 接口与实现 聚合 访问控制 类间的关联性 顺序图 处理流程和对象间的协调 时序图 Introduction 1 Java语言的线程 Java语言的线程 何 ...
- java 线程分组_Java多线程可以分组,还能这样玩!
前面的文章,栈长和大家分享过多线程创建的3种方式<实现 Java 多线程的 3 种方式>. 但如果线程很多的情况下,你知道如何对它们进行分组吗? 和 Dubbo 的服务分组一样,Java ...
最新文章
- 写给小白看的线程和进程,高手勿入
- python3示例_Python3 实例(七)
- plsql视图添加表字段_教你不到两分钟,快速创建数据透视图
- 安卓分辨率_安卓界面的尺寸规范有哪些?
- ICCV 2021 揭榜!十大方向抢先看!(Transformer/分割/Action/插帧/超分等)
- DEDE友情链接修改
- JPack插件停止更新,希望玩wow的朋友可以继续开发这个插件
- 把ts自动合并 下载网页视频并自动合成视频
- jeesite应用实战(数据增删改查),认真读完后10分钟就能开发一个模块
- MySQL入门推荐书籍
- 人工智能知识全面讲解:最简单的神经元模型
- 抖音直播睡觉一晚赚7.6万,心态崩了
- 清除tomcat缓存HTML,清除Tomcat缓存
- 计算机网络为什么要分层,从形而上到形而下视角的理解
- MYIP网站信息状态条 v1.0.1.3 (MyIP Status Bar for IE)
- 教你用Python自制拼图小游戏,一起来玩吧
- 虚拟机登录MySQL
- OWC生成统计报表(柱形图)
- 元宇宙中的手势交互(四)第一款主流VR头显中的手势交互原理剖析(Meta Quest 2)
- 2019级C语言大作业 - 泡泡龙