博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA并发编程学习:共享对象
阅读量:6554 次
发布时间:2019-06-24

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

hot3.png

可见性

  在一个单线程程序中,如果向一个变量先写入值,然后在没有写干涉的情况下读取这个变量,会得到相同的返回值。但是当读和写发生在不同的线程中时,就不能保证读线程及时地读取其他线程写入的值。在JAVA中所有实例域、静态域和数组元素存储在堆内存中,堆内存在线程之间共享,局部变量,方法定义参数和异常处理器参数不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响。为了确保跨线程写入的内存可见性,必须使用同步机制

  下例是主线程和读线程两个线程访问共享变量ready和number。主线程启动读线程,然后把number的值设为42,ready的值赋为true。读线程进行循环,直到发现ready的值变为true,然后打印出number的值。虽然看起来会输出42,但事实上,它很有可能打印出0,或者根本不会终止。这是因为它没有使用恰当的同步机制,没能保证主线程写入ready和number的值对读线程是可见的

package com.henrysun.javaSE.bfbc;/** * 在没有同步的情况下共享变量(不要这样做) * 重排序现象 * @author Sam Flynn * */public class GongXiangBianLiangReordering {	private static boolean ready;	private static int	number;		private static class ReaderThread extends Thread	{		public void run()		{			while(!ready)			{				Thread.yield();			}			System.out.println(number);		}	}		/**	 * @param args	 */	public static void main(String[] args) {	   new ReaderThread().start();	   number=42;	   ready=true;	}}

重排序现象

  上面的例子中,可能会打印0,因为早在对number赋值之前,主线程就已经写入ready并使之对读线程可见,这叫做。

103626_flld_1169289.png

  这里处理器A和处理器B可以同时把共享变量写入自己的写缓冲区(A1,B1),然后从内存中读取另一个共享变量(A2,B2),最后才把自己写缓存区中保存的脏数据刷新到内存中(A3,B3)。当以这种时序执行时,程序就可以得到x = y = 0的结果

锁和可见性

  锁不仅仅是关于同步互斥的,也是关于内存可见的。当线程A执行一个同步块时,线程B也随后进入了被同一个锁监视的同步块中,这时可以保证,在锁释放之前对A的可见的变量的值,B获得锁之后同样是可见的。换句话说,当B执行到A相同的锁监视的同步块时,A在同步块之中或之前所做的每件事,对B都是可见的。为了保证所有线程都能够看到共享的,可变变量的最新值,读取和写入线程必须使用公共的锁进行同步

Volatile变量

  当一个域声明为volatile类型后,编译器与运行时会监视这个变量:它是共享的,而且对它的操作不会与其他的内存操作一起被重排序。volatile变量不会缓存在寄存器或者缓存在其他对处理器隐藏的地方。所以,读一个volatile类型的变量时,总会返回由某一线程写入的最新值

  但是volatile变量的操作不会加锁,也就不会引起执行线程的阻塞,所以它只是轻量级的同步机制,正确使用volatile变量的方式包括:用于确保它们所引用的对象状态的可见性,或者用于标识重要的生命周期事件(比如初始化或关闭)的发生,即通常被当作标识完成、中断、状态的标记使用

  尽管volatile也可以用来标识其他类型的状态信息,但是决定这样做之前请格外小心。比如,volatile的语义不足以使自增操作(count++)原子化,除非你能保证只有一个线程对变量执行写操作。加锁可以保证可见性与原子性,而volatile变量只能保证可见性

  只有满足了下面的标准,才能使用volatile变量

  • 写入变量时并不依赖变量的当前值;或者能够确保只有单一的线程修改变量的值
  • 变量不需要与其他的状态变量共同参与不变约束
  • 而且,访问变量时,没有其他的原因需要加锁

ThreadLocal

  线程本地变量,详见

转载于:https://my.oschina.net/HerrySun/blog/738489

你可能感兴趣的文章
oracle中的三种异常情况
查看>>
6.18 学习记录
查看>>
centos6.7下网络设置
查看>>
[Android四大组件之二]——Service
查看>>
趋中法则
查看>>
一首《人道》,献给正在辛苦加班的程序员朋友们
查看>>
记录关于使用ADO.NET 连接池连接Oracle时Session信息不更新的坑
查看>>
nodejs windows下安装运行
查看>>
基于JavaMail的Java邮件发送:简单邮件发送
查看>>
maven引用net.sf.json-lib
查看>>
Spring IOC容器的初始化流程
查看>>
51Nod 1199 Money out of Thin Air(dfs序加线段树)
查看>>
Scrum立会报告+燃尽图(十一月二十三日总第三十一次):界面修改及新页面添加...
查看>>
实验二 20145237 20155226 2015234 实验报告 固件程序设计
查看>>
redis新手入门,摸不着头脑可以看看<一>
查看>>
1165: 零起点学算法72——首字母变大写
查看>>
动态规划--01背包问题
查看>>
Keepalived+LVS+Nginx负载均衡之高可用
查看>>
bzoj3232
查看>>
zorka源码解读之Beanshell与zorka的交互实现
查看>>