博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程之内存可见性
阅读量:5765 次
发布时间:2019-06-18

本文共 4838 字,大约阅读时间需要 16 分钟。

hot3.png

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 < count; i++) {			new Thread(new Runnable() {				@Override				public void run() {					demo.increase();					cdl.countDown();				}			}).start();		}		cdl.await();		System.out.println(demo.getResult());	}}

Console 输出: 500

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

 

2.

 

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 < count; 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 类实现共享变量在线程中的原子性( )

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 < count; 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 < count; 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 < count; 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 变量具备可见性, 不具备原子性

 

http://my.oschina.net/u/2503731/blog/671283

转载于:https://my.oschina.net/xiaominmin/blog/1599040

你可能感兴趣的文章
openstack G版 修改vm的flavor级别
查看>>
python_控制台输出带颜色的文字方法
查看>>
Android组件化最佳实践 ARetrofit原理
查看>>
舍弃浮躁, 50条重要的C++学习建议
查看>>
同步手绘板——将View的内容映射成Bitmap转图片导出
查看>>
【Android游戏开发之十】(优化处理)详细剖析Android Traceview 效率检视工具!分析程序运行速度!并讲解两种创建SDcard方式!...
查看>>
微信小程序之wx.navigateback往回携带参数
查看>>
陌陌和请吃饭之类的应用,你要是能玩转,那就厉害了
查看>>
递归的运行机制简单理解
查看>>
汉字转阿斯克马值
查看>>
Java 栈与堆简介
查看>>
【supervisord】部署单进程服务的利器
查看>>
zabbix oracle监控插件orabbix部署安装
查看>>
python3 通过qq 服务器 发送邮件
查看>>
部署Replica Sets及查看相关配置
查看>>
倒序显示数组(从右往左)
查看>>
文献综述二:UML技术在行业资源平台系统建模中的应用
查看>>
Swift 学习 用 swift 调用 oc
查看>>
第三章 Python 的容器: 列表、元组、字典与集合
查看>>
微信小程序开发 -- 点击右上角实现转发功能
查看>>