`
david.org
  • 浏览: 155165 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

线程安全性-volatile

阅读更多

今晚读了新同事关于P2P做种的Java 代码,Code风格很漂亮,但仔细多看几眼,就会出现瑕疵。其中有一点关于线程的安全性,还是觉得有必要拿出来说说。

代码的意图是利用一个标识性Boolean变量来控制一个线程的开启(Service)与结束(Shutdown)。
A类:
private Boolean isStarted = false;

public void start () {
	isStarted = true;
	while(isStarted){
		// do something
}
}

public void stop() {
	isStarted = false;
}


看完上段代码,不少朋友应该知道问题出现在了什么地方。没错,A类是线程不安全的,其他线程对isStarted 变量的修改,对于A线程类来说,会发生不可预知的问题。

对象的可见性
在一个单线程化的环境里,如果向一个变量先写入值,然后在没有写干涉的情况下读取这个变量,那么会得到预料之中的相同返回值。但是当读和写发生在不同的线程中时,通常不能保证读线程及时地读取其他线程写入的值,甚至可以说根本不可能。为了确保跨线程写入的内存可见性,需使用同步机制。
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);
		}
	}

	public static void main(String[] args){
		new ReaderThread().start();
		number = 42;
		ready = true;
	}


上段代码看起来最终执行的结果会是输出42,但事实上,它很有可能会输出0,甚至永远不会终止。这是因为没有使用恰当的同步机制。也就是说没能保证number和ready 的值对读线程是可见的。

很奇怪吧,但事实确实如此,因为对于读线程来说,ready 的值可能永远不可见,甚至会输出0,因为在对number赋值之前,主线程就已经写入ready,并对读线程写见。Okay,这是一种“重排序”(在单线程中,只要重排序不会对结果产生影响,那就不能完全保证其中的操作一定是按照程序所写的那样。 这是JVM的编译器为了充分利用多核处理器性能而设计的,这方面不了解的朋友可以了解一下Java存储模型)。

这种情况在我的项目中发生,是非常的可怕的。幸运的是被发现了,且Java提供了一个简单的方法来避免这个问题。

Volatile变量

Java提供了一种同步的弱形式(这也是一种以锁的形式出现的):volatile变量。它确保对一个变量的更新以可预见的方式通知其他的线程。当一个域声明为volatile类型后,编译器和运行时会监视这个变量:它是共享的,而且对它的操作不会与其他的内存操作一起被重排序。
volatile boolean asleep;
	...
		while (!asleep)
		{
			countSomeSheep();
		}

一个典型的volatile变量应用:检查状态标记,以确定是否退出一个循环。为了保证执行检查的线程能够注意到asleep已被其他线程修改。Asleep标记就必须为volatile。Okay, 这里同样可以使用锁来代替volatile,一样能保证对asleep变量修改的可见性。

最后说说volatile与锁的区别:
加锁可以保证可见性与原子性,然而volatile变量只能保证可见性
分享到:
评论

相关推荐

    java多线程安全性基础介绍.pptx

    java多线程安全性基础介绍 线程安全 正确性 什么是线程安全性 原子性 竞态条件 i++ 读i ++ 值写回i 可见性 JMM 由于cpu和内存加载速度的差距,在两者之间增加了多级缓存导致,内存并不能直接对cpu可见。 ...

    线程安全、volatile关键字、原子性、并发包、死锁、线程池.md

    线程安全、volatile关键字、原子性、并发包、死锁、线程池学习笔记

    线程安全在Java中的应用与实践.pptx.pptx

    synchronized关键字是Java提供的一种内建的同步机制,它可以确保同一时刻最多只有一个线程执行该段代码,从而保证线程安全。 volatile关键字 volatile关键字可以保证变量的可见性,当一个线程修改了volatile变量的值...

    并发编程基础知识,java内存模型及多线程、volatile

    ● volatile并不能真正的保证线程安全。它只能确保⼀个线程修改了数据后,其他线程能够看到这个改 动 当我们使⽤volatile去申明变量时,就等于告诉了虚拟机,这个变量极有可能会被某些程序或者线程 修改。为了确保这...

    volatile与synchronized的区别

    volatile与synchronized的区别,锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)

    concurrent 多线程 教材

    13 Java 理论与实践: 描绘线程安全性 (2) 14 编写高效的线程安全类 (2) 15 轻松使用线程 同步不是敌人.mht 16 轻松使用线程 减少争用.mht 17 轻松使用线程 不共享有时是最好的.mht 18 适用于 Java 程序员的 CSP...

    Java并发编程相关源码集 包括多任务线程,线程池等.rar

     volatile关键字的非原子性、volatile关键字的使用、AtomicInteger原子性操作、线程安全小例子:多个线程竞争问题、多个线程多个锁问题、创建一个缓存的线程池、多线程使用Vector或者HashTable的示例(简单线程同步...

    Volatile的正确使用1

    简介:Java语言包含两种内在的同步机制:同步块(或方法)和volatile变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同

    java面试题-java-interview-questions-master.zip

    出现线程安全问题的原因一般都是三个原因: 1、 线程切换带来的原子性问题 解决办法:使用多线程之间同步synchronized或使用锁(lock)。 2、 缓存导致的可见性问题 解决办法:synchronized、volatile、LOCK,可以...

    Java并发编程实战

    第2章 线程安全性 2.1 什么是线程安全性 2.2 原子性 2.2.1 竞态条件 2.2.2 示例:延迟初始化中的竞态条件 2.2.3 复合操作 2.3 加锁机制 2.3.1 内置锁 2.3.2 重入 2.4 用锁来保护状态 2.5 活跃性与性能 第...

    Java 并发编程实战

    2.1 什么是线程安全性 2.2 原子性 2.2.1 竞态条件 2.2.2 示例:延迟初始化中的竞态条件 2.2.3 复合操作 2.3 加锁机制 2.3.1 内置锁 2.3.2 重入 2.4 用锁来保护状态 2.5 活跃性与性能 第3章 对象的共享 ...

    线程安全之可见性揭秘-《云课堂》

    线程安全之可见性问题 JVM运行时数据区和JMM(java内存模型)有什么区别? 1. JVM运行时数据区 是 由 《==Java虚拟机规范==》定义的 理解:不同的JVM厂商都必须 遵循 《==Java虚拟机规范==》进行开发 2. JMM是由 ...

    volatile相关理论知识

    初步认识 Volatile、从硬件层面了解可见性的本质、JMM、HappenBefore;初步认识 Volatile、从硬件层面了解可见性的本质、JMM、HappenBefore;

    java进阶13天资料.zip

    day07-异常,线程的创建方式,,线程安全,线程同步 day08-线程状态,volatile关键字、原子性、并发包、死锁、线程池 day09-方法引用, Stream流,File类 , 递归 ,字节流 day10-字符流, 缓冲流、转换流、序列化流,...

    Java高并发编程详解:多线程与架构设计 (Java核心技术系列)

    部分主要阐述Thread的基础知识,详细介绍线程的API使用、线程安全、线程间数据通信,以及如何保护共享资源等内容,它是深入学习多线程内容的基础。 第二部分引入了ClassLoader,这是因为ClassLoader与线程不无关系,...

    java7hashmap源码-Concurrency:这是用来学习java多线程的

    可见性-volatile 通过内存屏障和禁止重排序优化实现 1.对volatile变量写操时,会在写操作后加入一条store屏障指令,将本地内存中的共享变量值刷新到主内存 2.对volatile变量读操时,会在读操作后加入一条load屏障指令,...

    java中的并发和多线程编程中文版

    读者将通过使用java.lang.thread类、synchronized和volatile关键字,以及wait、notify和notifyall方法,学习如何初始化、控制和协调并发操作。此外,本书还提供了有关并发编程的全方位的详细内容,例如限制和同步、...

    Java中的Runnable接口最全讲义

    6.1 线程安全性概述 6.2 使用同步方法 6.3 使用同步块 6.4 使用Lock和Condition 6.5 使用volatile关键字 7. 线程通信: 7.1 使用wait()和notify()方法 7.2 使用Lock和Condition 8. 线程池: 8.1 线程池的概述 8.2 ...

    手写一个线程安全的非阻塞队列,并且实现入队出队方法

    新建一个节点,加入到队尾,加入到队尾的操作,利用CAS原理加乐观锁,另外,还需要将新加的节点设置为新的队尾,此步操作也需要利用CAS,head与tail变量用volatile修饰,保证多线程环境下的线程可见性。 相关代码...

    Java并发编程原理与实战

    从Java字节码的角度看线程安全性问题.mp4 synchronized保证线程安全的原理(理论层面).mp4 synchronized保证线程安全的原理(jvm层面).mp4 单例问题与线程安全性深入解析.mp4 理解自旋锁,死锁与重入锁.mp4 深入...

Global site tag (gtag.js) - Google Analytics