线程基础知识系列(五)认识volatile-阿里云开发者社区

开发者社区> 技术小阿哥> 正文

线程基础知识系列(五)认识volatile

简介:
+关注继续查看

本篇文章主要讨论的关键字是volatile.

  1. volatile使用场景

  2. volatile介绍

  3. volatile vs synchronized vs lock

1 volatile使用场景

Volatile的官方定义

Java语言规范第三版中对volatile的定义如下: java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明成volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.threadexample.sysn;
public class ViolatileTest extends Thread {
    boolean keepRunning = true;
    public void run() {
        while (keepRunning) {
            //这儿不能进行打印操作,否则会影响演示
          //System.out.println("runing--");
        }
 
        System.out.println("Thread terminated.");
    }
 
    public static void main(String[] args) throws InterruptedException {
        ViolatileTest t = new ViolatileTest();
        t.start();
        Thread.sleep(1000);
        t.keepRunning = false;
        System.out.println("keepRunning set to false.");
    }
}

这个例子,想达到的效果很简单,main线程休眠1秒,关闭t线程。可运行结果演示,线程t并没有按预期正常退出。

如果使用volatile关键字修饰keepRunning 关键字,可达到预期效果。为什么呢?

2.volatile介绍

volatile的中文意思是“不稳定,反复无常的”。但是,在java领域,被翻译成“可见性”。

在java中,当volatile用于一个作用域时,java保证如下:

2.1 保证有序性

java内存模型,支持指令的重排序。为什么重排序,主要是从性能优化层面考虑的。主要的重排序包括以下:

编译器优化重排序
指令级并行重排序
内存系统重排序

重排序的结果:不会改变执行结果;从多线程角度看,其他线程执行顺序是无序的,从单线程角度看,本线程内的执行顺序是有序的。volatile限制了重排序。volatile的读和写建立了一个happens-before关系,类似于申请和释放一个互斥锁,与互斥锁的不同点是:不能像锁一样保证原子性访问。

2.2 保证可见性

根据JMM规定,每个工作线程分别持有本地缓存。线程之间的信息通信,均需要线程的本地缓存与共享缓存进行同步。

volatile声明的变量,不需要保存到本地缓存,也就是说每个线程访问一个volatile作用域时会在继续执行之前读取它的当前值,而不是(可能)使用一个缓存的值。

3.volatile vs synchronized vs lock

volatile与synchronized关键字,在功能上有些类似,但也有很多的不同。我个人比较喜欢通过对比的方式来学习,容易加深理解。

上面的演示例子,不使用volatile关键字也是可以的,可以使用同步synchronized。

3.1 使用synchronized,实现定时关闭线程效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.threadexample.sysn;
public class ViolatileTest extends Thread {
    private boolean keepRunning = true;
    public void run() {
        while (keepRunning) {
            System.out.println("runing--");
        }
 
        System.out.println("Thread terminated.");
    }
 
    public synchronized void setRunning(boolean keepRunning){
        this.keepRunning=keepRunning;
    }
 
    public static void main(String[] args) throws InterruptedException {
        ViolatileTest t = new ViolatileTest();
        t.start();
        Thread.sleep(1000);
        t.setRunning(false);
//        t.keepRunning = false;
        System.out.println("keepRunning set to false.");
    }
}


方面
volatile synchronized or lock
线程安全保障
仅仅保证可见性
可见性+原子性
相对并发性

独占锁,相对低

使用场景

1.对变量的写入操作,不依赖变量的当前值

2.该变量不会与其他状态变量一起纳入不变性条件中

3.访问变量时,不需要加锁


访问共享可变变量时

尽量考虑使用并发容器


1.使用violatile保证线程安全例子,这种方式是错误的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.threadexample.sysn;
import java.util.concurrent.TimeUnit;
public class CountTask implements Runnable {
    private volatile  int count;
    public void run() {
        for(int i=0;i<100;i++){
            count++;
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public int getCount(){return  this.count;}
    public static void main(String[] args) throws InterruptedException {
        CountTask ct = new CountTask();
        Thread t1 = new Thread(ct);
        Thread t2 = new Thread(ct);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(ct.getCount());
    }
}

这种方式不是线程安全的,因为存在状态依赖。递增操作,依赖于上一个值。




本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1795257,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
C#编程-145:Threading线程基础
C#编程-145:Threading线程基础
15 0
重新想象 Windows 8 Store Apps (48) - 多线程之其他辅助类: SpinWait, SpinLock, Volatile, SynchronizationContext, CoreDispatcher, ThreadLocal, ThreadStaticAttribute
原文:重新想象 Windows 8 Store Apps (48) - 多线程之其他辅助类: SpinWait, SpinLock, Volatile, SynchronizationContext, CoreDispatcher, ThreadLocal, ThreadStaticAttribut...
901 0
[CLR via C#]25. 线程基础
原文:[CLR via C#]25. 线程基础 一、Windows为什么要支持线程   Microsoft设计OS内核时,他们决定在一个进程(process)中运行应用程序的每个实例。进程不过是应用程序的一个实例要使用的资源的一个集合。
847 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
10548 0
多线程基础
需要了解的概念 并发和并行 并发侧重于任务的交替执行,同一时间只能执行一个任务;而并行是任务的同时执行,统一时间可以有多个任务被执行。 单核CPU与多核CPU下任务表现分别为并发与并行。 临界区 临界区用于表示一种公共资源或是共享数据,可以被多个线程使用,但是同一时间内,只能有一个线程在使用它。
691 0
C#基础知识回顾--线程传参
  在不传递参数情况下,一般大家都使用ThreadStart代理来连接执行函数,ThreadStart委托接收的函数不能有参数, 也不能有返回值。如果希望传递参数给执行函数,则可以使用带参数的ParameterizedThreadStart委托,           public delegat...
727 0
Java 线程基础必备知识
什么是进程?简言之,进程可视为一个正在运行的程序。它是系统运行程序的基本单位,因此进程是动态的。进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动。进程是操作系统进行资源分配的基本单位。
707 0
13694
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载