Java并发——线程同步Volatile与Synchronized详解

简介:

版权声明:本文为博主原创文章,未经博主允许不得转载。

0. 前言

转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52370068

面试时很可能遇到这样一个问题:使用volatile修饰int型变量i多个线程同时进行i++操作,这样可以实现线程安全吗?提到线程安全、线程同步,我们经常会想到两个关键字:volatilesynchronized,那么这两者有什么区别呢?

 

1. volatile修饰的变量具有可见性

volatile是变量修饰符,其修饰的变量具有可见性。

可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值。

Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或是CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存

例子请查看下面的3.1,帮助理解。


2. volatile禁止指令重排 

volatile可以禁止进行指令重排

指令重排是指处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证各个语句的执行顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性

程序执行到volatile修饰变量的读操作或者写操作时,在其前面的操作肯定已经完成,且结果已经对后面的操作可见,在其后面的操作肯定还没有进行

例子请查看下面3.2,帮助理解。


3.  synchronized 

synchronized可作用于一段代码或方法,既可以保证可见性,又能够保证原子性。

可见性体现在:通过synchronized或者Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存中。

原子性表现在:要么不执行,要么执行到底。

例子请查看下面3.3,帮助理解。

 

2. 总结
(1从而我们可以看出volatile虽然具有可见性但是并不能保证原子性。

2)性能方面,synchronized关键字是防止多个线程同时执行一段代码,就会影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized。

但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。

 

3. volatilesynchronized的使用场景举例(结合第1部分进行理解学习)

3.1 volatile的使用举例

1
<br>

线程执行run()的时候我们需要在线程中不停的做一些事情,比如while循环,那么这时候该如何停止线程呢?如果线程做的事情不是耗时的,那么只需要使用一个标志即可。如果需要退出时,调用setStop()即可。这里就使用了关键字volatile,这个关键字的目的是如果修改了isStop的值,那么while循环中可以立即读取到修改后的值

如果线程做的事情是耗时的,那么可以使用interrupt方法终止线程 。如果在子线程“睡觉”时被interrupt,那么子线程可以catch到InterruptExpection异常,处理异常后继续往下执行。

 

3.2 volatile的使用举例

1
2
3
4
5
6
7
8
9
//线程1:  
context = loadContext();    //语句1  context初始化操作  
inited =  true ;              //语句2  
    
//线程2:  
while (!inited ){  
   sleep()  
}  
doSomethingwithconfig(context);

因为指令重排序,有可能语句2会在语句1之前执行,可能导致context还没被初始化,而线程2中就使用未初始化的context去进行操作,导致程序出错。

这里如果volatile关键字对inited变量进行修饰,就不会出现这种问题了

 

3.3 必须使用synchronized而不能使用volatile的场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  class  Test {  
     public  volatile  int  inc =  0 ;  
     public  void  increase() {  
         inc++;  
     }  
        
     public  static  void  main(String[] args) {  
         final  Test test =  new  Test();  
         for ( int  i= 0 ;i< 10 ;i++){  
             new  Thread(){  
                 public  void  run() {  
                     for ( int  j= 0 ;j< 1000 ;j++)  
                         test.increase();  
                 };  
             }.start();  
         }  
            
         while (Thread.activeCount()> 1 )   //保证前面的线程都执行完  
             Thread.yield();  
         System.out.println(test.inc);  
     }  
}

例子中用new10个线程,分别去调用1000increase()方法,每次运行结果都不一致,都是一个小于10000的数字。自增操作不是原子操作,volatile 是不能保证原子性的。回到文章一开始的例子,使用volatile修饰int型变量i,多个线程同时进行i++操作。比如有两个线程ABvolatile修饰的i进行i++操作,i的初始值是0A线程执行i++时刚读取了i的值0,就切换到B线程了,B线程(从内存中)读取i的值也为0,然后就切换到A线程继续执行i++操作,完成后i就为1了,接着切换到B线程,因为之前已经读取过了,所以继续执行i++操作,最后的结果i就为1了。同理可以解释为什么每次运行结果都是小于10000的数字。
但是使用synchronized对部分代码进行如下修改,就能保证同一时刻只有一个线程获取锁然后执行同步代码。运行结果必然是10000。

1
2
3
4
public   int  inc =  0 ;  
public  synchronized  void  increase() {  
         inc++;  
}


本文整理参考自:
http://www.cnblogs.com/dolphin0520/p/3920373.html以及warmor的博客








本文转自yunlielai51CTO博客,原文链接:http://blog.51cto.com/4925054/2083726 ,如需转载请自行联系原作者

相关文章
|
3月前
|
Java 大数据 Go
从混沌到秩序:Java共享内存模型如何通过显式约束驯服并发?
并发编程旨在混乱中建立秩序。本文对比Java共享内存模型与Golang消息传递模型,剖析显式同步与隐式因果的哲学差异,揭示happens-before等机制如何保障内存可见性与数据一致性,展现两大范式的深层分野。(238字)
112 4
|
3月前
|
缓存 安全 Java
如何理解Java中的并发?
Java并发指多任务交替执行,提升资源利用率与响应速度。通过线程实现,涉及线程安全、可见性、原子性等问题,需用synchronized、volatile、线程池及并发工具类解决,是高并发系统开发的关键基础。(238字)
274 5
|
3月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
230 1
|
3月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
246 1
|
4月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
199 0
|
4月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
356 16
|
5月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
5月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
6月前
|
SQL 缓存 安全
深度理解 Java 内存模型:从并发基石到实践应用
本文深入解析 Java 内存模型(JMM),涵盖其在并发编程中的核心作用与实践应用。内容包括 JMM 解决的可见性、原子性和有序性问题,线程与内存的交互机制,volatile、synchronized 和 happens-before 等关键机制的使用,以及在单例模式、线程通信等场景中的实战案例。同时,还介绍了常见并发 Bug 的排查与解决方案,帮助开发者写出高效、线程安全的 Java 程序。
367 0