线程基础知识系列(五)认识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,如需转载请自行联系原作者

相关文章
|
2月前
|
存储 安全 Java
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
72 3
|
4月前
|
存储 缓存 Java
volatile 与线程的那些事
volatile 与线程的那些事
22 0
|
4月前
|
设计模式 安全 编译器
线程学习(3)-volatile关键字,wait/notify的使用
线程学习(3)-volatile关键字,wait/notify的使用
28 0
|
5月前
|
存储 安全 Java
关于volatile解决内存可见性问题(保证线程安全)
关于volatile解决内存可见性问题(保证线程安全)
|
6月前
|
Java 调度 C++
2 线程基础知识复习
2 线程基础知识复习
|
6月前
|
Java 程序员 调度
【Java之家-编程的衣柜】线程的基础知识及线程与进程的联系
【Java之家-编程的衣柜】线程的基础知识及线程与进程的联系
37 0
|
9月前
|
缓存 Java 编译器
【Java|多线程与高并发】volatile关键字和内存可见性问题
synchronized和volatile都是Java多线程中很重要的关键字,但它们的作用和使用场景有所不同。
|
10月前
|
缓存 Java 程序员
【Java基础】线程相关基础知识
cpu核心数指cpu 内核数量,如双核、四核、八核。 cpu线程数是一种逻辑的概念,就是模拟出的cpu核心数,
|
11月前
|
Java 编译器
volatile关键词在多线程中的应用
volatile关键词在多线程中的应用
37 0
volatile关键词在多线程中的应用
|
12月前
|
缓存 编译器 Linux
【Linux】volatile | SIGCHLD | 多线程概念
【Linux】volatile | SIGCHLD | 多线程概念
56 0