Java:多线程中的volatile
一、为什么使用volatile
首先,通过一段简单的代码来理解为什么要使用volatile:
public class RunThread extends Thread{
private boolean isRunning = true;
public boolean isRunning(){
return isRunning;
}
public void setRunning(boolean isRunning){
this.isRunning = isRunning;
}
@Override
public void run() {
System.out.println("进入run...");
while(isRunning==true){}
System.out.println("线程停止了");
}
}
import java.util.concurrent.TimeUnit;
public class Run {
public static void main(String[] args){
try{
RunThread thread = new RunThread();
thread.start();
TimeUnit.MILLISECONDS.sleep(1000);
thread.setRunning(false);
System.out.println("赋值为false");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果:
我们创建了一个RunThread对象,并启动了该线程。当 isRunning == True时,将一直进行while循环。可以看到,我们启动了线程之后就调用setRunning方法将isRunning设为false。
按道理来说,当isRunning的状态为false时,就会退出while循环,输出“赋值为false”。然后run方法执行结束,线程执行完毕,整个程序运行结束。
但通过执行结果发现,线程进入了while死循环,线程从未终止。

这是由于在启动RunThread.java线程时,变量 private boolean isRunning = True 存在于公共堆栈和线程的私有堆栈中。JVM以server模式运行时,为了提高运行效率,线程一直从私有堆栈中读取isRunning的值为True。而thread.setRunning(false)执行后,是把公共堆栈中的isRunning改为false。所以即便将isRunning改为false,程序依然进入了while死循环。
解决办法:
使用volatile关键字,把 private boolean isRunning = True 改为 volatile private boolean isRunning = True ,再次运行:

volatile的作用是:强制线程从公共堆栈中取变量的值,而非从线程的私有堆栈中取值


二、 volatile的非原子性
1. 虽然volatile关键字增加了实例变量在多个线程之间的可见性,但它不具备同步性,也就是原子性。
public class MyThread extends Thread{
volatile public static int count;
private static void addCount(){
for(int i =0;i<100;i++){
count++;
}
System.out.println("count= "+count);
}
@Override
public void run() {
addCount();
}
}
public class Run {
public static void main(String[] args) {
MyThread[] mythreadArray = new MyThread[100];
for (int i = 0; i < 100; i++) {
mythreadArray[i] = new MyThread();
}
for (int i = 0; i < 100; i++) {
mythreadArray[i].start();
}
}
}
执行结果:

volatile保证了线程每次从公共内存中读取变量,保证了同步数据的可见性。但由上述代码可以看出,使用关键字volatile时出现了非线程安全问题。i++ 操作并非一个原子操作,可以分解为:
(1)从内存读取 i 的值;
(2)计算 i 的值;
(3)将 i 的值写入内存
当一个线程正在执行步骤(2)时,另一个线程也可能也在修改 i 值,就会出现脏数据。
2. 解决办法
(1)方法一:使用synchronized关键字
当在 private static void addCount() 前面加上关键字synchronized后 synchronized private static void addCount() ,再次执行:

当时用了synchronized关键字时,就不必再使用volatile关键字了。
(2)方法二:使用原子类进行i++操作
public class MyThread extends Thread{
private static AtomicInteger count = new AtomicInteger(0);
synchronized private static void addCount(){
for(int i =0;i<100;i++){
count.incrementAndGet();
}
System.out.println("count= "+count);
}
@Override
public void run() {
addCount();
}
}
注意:原子类并不完全安全,只有原子类的increamentAndGet( )方法是原子的,不保证方法与方法之间的调用也是原子的。
Java:多线程中的volatile的更多相关文章
- java多线程中的volatile和synchronized
package com.chzhao; public class Volatiletest extends Thread { private static int count = 0; public ...
- 彻底弄明白之java多线程中的volatile
一. volatite 简述 Java 语言提供了一种稍弱的同步机制,即 volatile 变量.用来确保将变量的更新操作通知到其他线程,保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新. ...
- java多线程中的三种特性
java多线程中的三种特性 原子性(Atomicity) 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行. 如果一个操作时原子性的,那么多线程并 ...
- java 多线程中的wait方法的详解
java多线程中的实现方式存在两种: 方式一:使用继承方式 例如: PersonTest extends Thread{ String name; public PersonTest(String n ...
- java多线程中并发集合和同步集合有哪些?区别是什么?
java多线程中并发集合和同步集合有哪些? hashmap 是非同步的,故在多线程中是线程不安全的,不过也可以使用 同步类来进行包装: 包装类Collections.synchronizedMap() ...
- java多线程中最佳的实践方案是什么?
java多线程中最佳的实践方案是什么? 给你的线程起个有意义的名字.这样可以方便找bug或追踪.OrderProcessor, QuoteProcessor or TradeProcessor 这种名 ...
- Java多线程中的常用方法
本文将带你讲诉Java多线程中的常用方法 Java多线程中的常用方法有如下几个 start,run,sleep,wait,notify,notifyAll,join,isAlive,current ...
- Java多线程中的竞争条件、锁以及同步的概念
竞争条件 1.竞争条件: 在java多线程中,当两个或以上的线程对同一个数据进行操作的时候,可能会产生“竞争条件”的现象.这种现象产生的根本原因是因为多个线程在对同一个数据进行操作,此时对该数据的操作 ...
- Java多线程 -- 正确使用Volatile变量
Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”:与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少, ...
- Java多线程中的死锁
Java多线程中的死锁 死锁产生的原因 线程死锁是指由两个以上的线程互相持有对方所需要的资源,导致线程处于等待状态,无法往前执行. 当线程进入对象的synchronized代码块时,便占有了资源,直到 ...
随机推荐
- 关于PHP数组你应该知道的事情
(1).PHP数组的遍历顺序 先举个栗子: <?php $arr['a'] = '123'; $arr['b'] = '456'; $arr['c'] = '789'; foreach($a a ...
- 利用arduino给PCB800099液晶驱动板烧录程序
某宝上购买了一块PCB800099液晶驱动板, 卖家出货的时候刷的驱动不对,遂需要重新烧录程序 可是苦于没有编程器,寻遍网络后找到几种解决方案: arduino刷,树莓派I2C口刷,linux下用vg ...
- 【翻译】Django Channels 官方文档 -- Tutorial
Django Channels 官方文档 https://channels.readthedocs.io/en/latest/index.html 前言: 最近课程设计需要用到 WebSocket,而 ...
- 【题解】洛谷P1120 小木棍(搜索+剪枝+卡常)
洛谷P1120:https://www.luogu.org/problemnew/show/P1120 思路 明显是搜索题嘛 但是这数据增强不是一星半点呐 我们需要N多的剪枝 PS:需要先删去超出50 ...
- TCP Three-way Handshake
TCP Connection Management Out-of-Order and Duplication(复制) Problem Old segment from previous connect ...
- ovs的卸载
使用apt-get安装的openvswitch,然后卸载好麻烦撒,而且还没有相关资料说一下怎么删除的,特此记录一下,希望有个参考 首先把服务器上使用ovs新建的虚拟网桥全部删除掉 然后把原有的内核删除 ...
- 多python版本下,使用pip安装第三方库
说明:win10系统,先安装有Python3.5.2,后又安装了Python2.7.13(并重命名了Python27文件夹下python.exe为python2.7.13.exe),试图使用pip安装 ...
- 手机端app开发初识
1.所需软件说明 百度云下载链接: https://pan.baidu.com/s/1-TEQZP9QbJSlGSYedyAUFw密码: 2z8l 或者官方链接: Hbuilder:http://ww ...
- 一点一点看JDK源码(一)Collection体系概览
一点一点看JDK源码(一)Collection体系概览 liuyuhang原创,未经允许进制转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 Collection为集 ...
- HP-UNIX平台修改Oracle processes参数报错:ORA-27154、ORA-27300、ORA-27301、ORA-27302
OS 版本 :HP-UX B.11.31Oracle版本:11.2.0.4 (RAC) (一)问题描述 最近发现无法连接上数据库,报错信息为“ORA-00020:maximum number ...