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

线程基础知识系列(四)线程的同步2  :线程的notify-wait通信机制,以及Condition条件变量

线程基础知识系列(三)线程的同步  :同步控制,锁及synchronized

线程基础知识系列(二)线程的管理 :线程的状态,控制,休眠,Interrupt,yield等

线程基础知识系列(一)线程的创建和启动  :线程的创建和启动,join(),daemon线程,Callable任务。

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

  1. volatile使用场景
  2. volatile介绍
  3. volatile vs synchronized vs lock

1 volatile使用场景

Volatile的官方定义

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

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,实现定时关闭线程效果。

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保证线程安全例子,这种方式是错误的。

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());
    }
}

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

时间: 2024-12-24 18:21:39

线程基础知识系列(五)认识volatile的相关文章

线程基础知识系列(四)线程的同步2 线程通信和Condition变量

本文是系列的第四篇. 线程基础知识系列(三)线程的同步  :同步控制,锁及synchronized 线程基础知识系列(二)线程的管理 :线程的状态,控制,休眠,Interrupt,yield等 线程基础知识系列(一)线程的创建和启动  :线程的创建和启动,join(),daemon线程,Callable任务. 第三篇文章,重点阐述了如何使用锁和同步块对线程间共享可变变量保护,保证只有一个线程可以进入临界区.其实,没有过多的涉及另一个重要的同步概念:线程协作.第三篇中涉及的线程间并没有有效的协调.

线程基础知识系列(三)线程的同步

本文是系列的第三篇,前面2篇,主要是针对单个线程如何管理,启动等,没有过多涉及多个线程是如何协同工作的. 线程基础知识系列(二)线程的管理 :线程的状态,控制,休眠,Interrupt,yield等 线程基础知识系列(一)线程的创建和启动  :线程的创建和启动,join(),daemon线程,Callable任务. 本文的主要内容 何谓线程安全? 何谓共享可变变量? 认识synchronized关键字 认识Lock synchronized vs Lock 1.何谓线程安全 多线程是把双刃剑,带

线程基础知识系列(二)线程的管理

本篇是线程基础知识系列的第二篇,主要简单江夏线程管理相关知识点. 线程基础知识系列(一)线程的创建和启动:说明了线程的2种创建和启动,join(),daemon线程,Callable 任务. 本文的主要内容 线程的状态 线程的优先级 sleep vs wait 线程的流程控制 Interrupt yield让出你的CPU 1.线程的状态 以<线程基础知识系列(一)线程的创建和启动>这张图,是程序的运行时线程信息截图.有main线程,user Threads,daemon Threads.现在咱

线程基础知识

什么是线程: 在一个程序里的一个执行路线就叫做线程(thread).更准确的定义是:线程是"一个进程内部的控制序列" 一切进程至少都有一个执行线程 进程与线程 进程是资源竞争的基本单位 线程是程序执行的最小单位 线程共享进程数据,但也拥有自己的一部分数据 线程ID 一组寄存器 栈 errno 信号状态 优先级 fork和创建新线程的区别 当一个进程执行一个fork调用的时候,会创建出进程的一个新拷贝,新进程将拥有它自己的变量和它自己的PID.这个新进程的运行时间是独立的,它在执行时几乎

Python基础知识(五)

# -*- coding: utf-8 -*-# @Time : 2018-12-25 19:31# @Author : 三斤春药# @Email : [email protected]# @File : Python基础知识(五).py# @Software : PyCharm Python基础知识(五)今日内容大纲:昨日内容回顾 list: 增:append() insert(index,object) extend() 迭代着追加 删: pop 默认删除最后一个,按照索引去删除,有返回值.

Java__线程---基础知识全面实战---坦克大战系列为例

今天想将自己去年自己编写的坦克大战的代码与大家分享一下,主要面向学习过java但对java运用并不是很熟悉的同学,该编程代码基本上涉及了java基础知识的各个方面,大家可以通过练习该程序对自己的java进行一下实战. 每个程序版本代码中,都附有相关注释,看完注释大家就可以对本程序设计有个很明显的思路.真的很有趣,想对java重新温习的同学完全不必再对厚厚的java基础书籍进行阅读了,在跟着本次代码练习并分析后,大家一定会对java各方面基础知识 尤其是线程的知识有更深一步的了解!!! 本次坦克大

[C# 基础知识系列]专题一:深入解析委托——C#中为什么要引入委托

转自http://www.cnblogs.com/zhili/archive/2012/10/22/Delegate.html 引言: 对于一些刚接触C# 不久的朋友可能会对C#中一些基本特性理解的不是很深,然而这些知识也是面试时面试官经常会问到的问题,所以我觉得有必要和一些接触C#不久的朋友分享下关于C#基础知识的文章,所以有了这个系列,希望通过这个系列让朋友对C#的基础知识理解能够更进一步.然而委托又是C#基础知识中比较重要的一点,基本上后面的特性都和委托有点关系,所以这里就和大家先说说委托

基础知识《五》---Java多线程的常见陷阱

1.在构造函数中启动线程 我在很多代码中都看到这样的问题,在构造函数中启动一个线程,类似这样: 1 public class A{ 2 public A(){ 3 this.x=1; 4 this.y=2; 5 this.thread=new MyThread(); 6 this.thread.start(); 7 } 8 9 }   这个会引起什么问题呢?如果有个类B继承了类A,依据java类初始化的顺序,A的构造函数一定会在B的构造函数调用前被调用,那么thread线程也将在B被完全初始化之

基础知识系列?闲言

以前总是觉得... 写一些基础知识的博客·不就是抄书抄书嘛... 东抄西拷-- 并且发现基础知识就是这些内容嘛··· A写的和B写的都差不多·只是细节和深度不太一样··· 1 也曾问自己什么时候能写一点有深度的博客·装下X... 2 刚开始学习也是啃书...不过那时候确实没有经验吧. 3 啃书很吃力的说... 4 5 从中也知道了,人看书也要分境界的: 6 有些话·你读了不一定懂... 7 因为你根本没经历过(也说明了边读书边撸代码的重要性,需要自己去体会)··· 1 总之基础知识还是要好好慢慢