java中volatile关键字的含义
在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉。
Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制。
synchronized
同步块大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用
synchronized 修饰的方法 或者 代码块。
volatile
用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操作。
下面看一个例子,我们实现一个计数器,每次线程启动的时候,会调用计数器inc方法,对计数器进行加一
执行环境——jdk版本:jdk1.6.0_31 ,内存 :3G cpu:x86 2.4G
public class Counter {
public static int count = 0;
public static void inc() {
//这里延迟1毫秒,使得结果明显
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
count++;
}
public static void main(String[] args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Counter.inc();
}
}).start();
}
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" + Counter.count);
}
}
运行结果:Counter.count=995
实际运算结果每次可能都不一样,本机的结果为:运行结果:Counter.count=995,可以看出,在多线程的环境下,Counter.count并没有期望结果是1000
很多人以为,这个是多线程并发问题,只需要在变量count之前加上volatile就可以避免这个问题,那我们在修改代码看看,看看结果是不是符合我们的期望
public class Counter {
public volatile static int count = 0;
public static void inc() {
//这里延迟1毫秒,使得结果明显
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
count++;
}
public static void main(String[] args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Counter.inc();
}
}).start();
}
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" + Counter.count);
}
}
运行结果:Counter.count=992
运行结果还是没有我们期望的1000,下面我们分析一下原因
在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,
线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存
变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,
在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图
描述这写交互
相关推荐
在这个例子中,主线程设置了`ThreadLocal`变量的值,但每个子线程在自己的上下文中运行时,它们看到的`ThreadLocal`变量值是独立的。因此,`thread1`和`thread2`可能会打印出不同的值,因为它们拥有各自的副本。 ...
一张图方便理解和掌握java 多线程之间通信的实质 java 多线程 其实就是每个线程都拥有自己的内存空间,多线程之间的通信...锁的机制说白了,就是在获取共享变量的时候,增加了字段标识该字段是否已经被其他线程修改中
正确使用 volatile 变量的条件是:对变量的写操作不依赖于当前值,以及该变量没有包含在具有其他变量的不变式中。在实际应用中,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量...
Java线程安全是多线程编程中的一个关键概念,它涉及到在并发环境下如何正确地管理共享资源,确保程序的正确性和一致性。以下是对Java线程安全的深入总结: ### 一、线程安全的定义 线程安全是指当多个线程访问同一...
Java线程安全是指在多线程并发编程中,如何确保线程安全地访问和修改共享资源的问题。Java内存模型(JMM)是Java虚拟机(JVM)定义的内存模型,旨在屏蔽底层平台的内存管理细节,提供了一个统一的内存模型规范。 ...
这篇博客“Java线程间数据交换的疑惑”可能探讨了在并发编程中如何有效地共享和同步数据。`volatile`关键字是Java中用于实现线程间通信的一个重要工具,它在多线程环境下起着关键的作用。 首先,我们要理解`...
Java线程-Java内存模型是Java并发编程中的关键概念,它描述了多个线程如何共享和访问内存资源,以及如何保证数据的一致性和安全性。Java内存模型(JMM)是Java虚拟机规范的一部分,用于定义程序中各个线程对共享变量...
它确保了被volatile修饰的变量对所有线程都是可见的,即一旦一个线程修改了volatile变量,其他线程能立即看到这一变化,避免了数据不一致的情况。这是通过Java内存模型(JMM)实现的,JMM规定了线程如何访问共享内存...
不可变对象在多线程环境中具有天然的安全性,因为它们的值不会在并发访问时被意外修改。这使得多个线程可以共享一个不可变对象,无需担心数据一致性问题,从而提高了程序的并发性能。 三、Java多线程防止非安全问题...
线程需要访问和修改半径值,因此必须确保这些操作是线程安全的,防止数据竞争。 对于控制圆心位置的线程,可能需要不断地更新坐标值。可以定义两个变量代表x和y坐标,并通过线程同步机制确保在改变坐标时不会发生...
它能确保修改后的值能够立即对其他线程可见,但无法防止多个线程同时读写某个变量导致的不一致性问题。 Java 5之后,线程池的引入大大提升了线程管理的效率。ThreadPoolExecutor允许预定义一组线程,线程池中的线程...
即当一个线程修改了带有`volatile`修饰符的变量后,其他线程能立即看到最新的值。 - **锁**:除了内置的`synchronized`关键字之外,Java还提供了一些高级锁机制,如`ReentrantLock`等,这些锁具有更高的灵活性和更细...
例如,一个线程的构造函数完成`happens-before`这个线程的`start()`方法,这意味着其他线程在启动该线程后可以观察到构造函数中对共享变量的修改。`volatile`和`synchronized`同样可以提供一定的有序性保证。 在...
线程对变量的修改需要经过“读取-使用-修改-存储”这四个步骤,确保最终结果会写回主内存,使得其他线程可以看到更新后的值。JVM为此定义了read、load、use、assign、store和write等操作指令来协调这些操作。 其次...
- Java线程有优先级的概念,可以通过`setPriority()`方法设置线程的优先级。优先级高的线程在调度时更有可能被选中执行。但是这并不保证高优先级线程一定会先于低优先级线程执行,因为线程调度最终还是由操作系统...
当一个线程修改了一个带有`volatile`关键字的变量时,不仅会把修改后的值写回主内存,还会强制其他线程的工作内存中的副本无效,使得其他线程再次读取时必须从主内存中读取最新值。 - **final关键字**:当一个对象被...
1. **volatile关键字**:声明为volatile的变量,在被线程修改后会立刻刷新到主内存中,其他线程在读取时也会强制从主内存中读取最新的值。 2. **synchronized关键字**:同步代码块不仅提供了锁机制,还确保了锁释放...
如果一个变量被volatile修饰,那么所有线程都会看到最新修改的值。 6. Lock接口和ReentrantLock类:提供了比synchronized更精细的锁控制,如可中断的等待、公平锁等。ReentrantLock是Lock接口的一个实现,具有可重...