java多线程synchronized volatile解析
先简单说说原子性:具有原子性的操作被称为原子操作。原子操作在操作完毕之前不会线程调度器中断。即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。在Java中,对除了long和double之外的基本类型的简单操作都具有原子性。简单操作就是赋值或者return。比如”a = 1;“和 “return a;”这样的操作都具有原子性。但是在Java中,类似”a += b”这样的操作不具有原子性,不是同步的就会出现难以预料的结果。
在我们平常的编程过程中,经常会遇到线程安全与不全这类的词语,相信刚开始很多新手碰到这个都会很头疼。现拿万恶的火车票举个例子吧。如杭州到武汉还剩10张车票,有4个售票点正在售杭州到武汉的票,若所写程序是线程不安全的,则当a售票点售出票1时,b售票点也正在售票,由于a售票点售出后还未及时更新到主内存中,因此b售票点可能同样售出的是票1,这就造成了两个人买到一张座位的情况,这就是线程不安全的。若线程安全,则当a售票点在售票1时,只有等a售票点将票1售出,并更新了主内存数据后,其余售票点才能继续卖票,并且数据中票1已经卖掉了,不会再重复操作。此时即利用了synchronized锁的机制。
- synchronized是一个方法或块的修饰符,synchronized分为方法同步和块同步。
1、synchronized 实例方法
//加在方法上
public synchronized void function() {
//somecode
}
该同步是同步在拥有该方法的对象上的,同一时间只有一个线程能访问到该方法(前提是在一个实例上,若有几个实例,则可以同时进行)。
2、synchronized 静态方法
public static synchronized void function(){
//somecode
}
由于静态方法不属于对象,而是属于类,会将这个方法所在类的class对象上锁。
3、synchronized 块
//同步块
public void method1(){
synchronized(this){
//somecode
}
} public void method2(Object o){
synchronized(o){
//somecode
}
}
锁定的是调用这个方法的对象(method1)或传入的对象(method2),当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(对象)来充当锁。
- volatile是一个变量修饰符
一个volatile类型的变量不允许线程从主内存中将变量的值拷贝到自己的存储空间。因此,一个声明为volatile类型的变量将在所有的线程中同步的获得数据,不论你在任何线程中更改了变量,其他的线程将立即得到同样的结果。volatile变量具有synchronized的可见性特性,但是不具备原子特性。这就是说线程能够自动发现volatile变量的最新值。volatile变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。
正确使用volatile修饰符:a、对变量的写操作不依赖于当前值。b、该变量没有包含在具有其他变量的不变式中。
=========================================关于两者的区别==============================
1.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
3.volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。
4.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
5.volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。
一些例子:
synchronized
public class SyncTest {
public static void main(String[] args) {
Outputter outputter = new Outputter();
MyThread myThread1 = new MyThread(outputter, "zhangsan");
MyThread myThread2 = new MyThread(outputter, "lisi");
Thread threada = new Thread(myThread1);
Thread threadb = new Thread(myThread2);
threada.start();
threadb.start();
}
}
class MyThread implements Runnable {
Outputter outputter;
String name;
public MyThread(Outputter outputter, String name) {
this.outputter = outputter;
this.name = name;
}
@Override
public void run() {
outputter.output(name);
}
}
/**
* 当没有锁定时,将输出"zhlangisisan"等不确定的值。
*
* @author lcm
*
*/
class Outputter {
public void output(String name) {
// 为了保证对name的输出不是一个原子操作,这里逐个输出name的每个字符
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
}
}
/**
* 若想得到zhangsanlisi或者lisizhangsan,就需要用到synchronized。
*
* @author lcm
*
*/
class Outputter {
public synchronized void output(String name) {
// 为了保证对name的输出不是一个原子操作,这里逐个输出name的每个字符
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
}
}
class Outputter {
public void output(String name) {
// 为了保证对name的输出不是一个原子操作,这里逐个输出name的每个字符
synchronized (this) { //代码块也可以传入一个对象,但需保证是同一个对象
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
}
}
}
/**
* 若修饰静态静态代码块,则是对整个class对象加锁
*
* @author lcm
*
*/
class Outputter {
public synchronized static void output(String name) {
// 为了保证对name的输出不是一个原子操作,这里逐个输出name的每个字符
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
}
}
volatile 无法保证对变量操作的原子性
public class VolatileTest {
private volatile int inc = 0;
public static void main(String[] args) {
final VolatileTest volatileTest = new VolatileTest();
for (int i = 0; i < 1000; i++) {
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1);
volatileTest.inc();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
while (Thread.activeCount() > 1){
// 保证前面的线程都执行完
Thread.yield();
}
System.out.println(volatileTest.inc);
}
private void inc() {
inc++;
}
}
运行结果并不是1000,volatile关键字能保证可见性没有错,但是上面的程序错在没能保证原子性。可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性。
线程1对变量进行自增操作,线程1先读取了变量inc的原始值,然后线程1被阻塞了;然后线程2对变量进行自增操作,线程2也去读取变量inc的原始值,由于线程1只是对变量inc进行读取操作,而没有对变量进行修改操作,所以不会导致线程2的工作内存中缓存变量inc的缓存行无效,所以线程2会直接去主存读取inc的值,发现inc的值是10,然后进行加1操作,并把11写入工作内存,最后写入主存。然后线程1接着进行加1操作,由于已经读取了inc的值,注意此时在线程1的工作内存中inc的值仍然为10,所以线程1对inc进行加1操作后inc的值为11,然后将11写入工作内存,最后写入主存。那么两个线程分别进行了一次自增操作后,inc只增加了1。
//线程1
boolean stop = false;
while(!stop){
doSomething();
} //线程2
stop = true;
上述代码当线程2更改了stop变量的值之后,但是还没来得及写入主存当中,那么线程1由于不知道线程2对stop变量的更改,因此还会一直循环下去。
若加上volatile,会强制将修改的值立即写入主存,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。
java多线程synchronized volatile解析的更多相关文章
- Java 多线程 —— synchronized关键字
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- java多线程关键字volatile的使用
java多线程关键字volatile的作用是表示多个线程对这个变量共享. 如果是只读的就可以直接用,写数据的时候要注意同步问题. 例子: package com.ming.thread.volatil ...
- Java多线程编程——volatile关键字
(本篇主要内容摘自<Java多线程编程核心技术>) volatile关键字的主要作用是保证线程之间变量的可见性. package com.func; public class RunThr ...
- Java多线程synchronized同步
非线程安全问题 “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程问题”.也即是说,方法中的变量永远是线程安全的. 如果多个线程共同访问1个对象中的实例变量,则可能线程 ...
- Java多线程程序设计详细解析
一.理解多线程 多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立. 线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线 ...
- JAVA多线程synchronized详解
Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 当两个并发线程访问同一个对象object中的这个synchronized(this)同 ...
- java多线程-synchronized
一.线程安全问题 多线程操作各自线程创建的资源的时候,不存在线程安全问题.但多线程操作同一个资源的时候就会出现线程安全问题.下例为两个线程操作同一个name资源时发生的问题. class TestSy ...
- java多线程中 volatile与synchronized的区别-阿里面试
volatile 与 synchronized 的比较(阿里面试官问的问题) ①volatile轻量级,只能修饰变量.synchronized重量级,还可修饰方法 ②volatile只能保证数据的可见 ...
- java多线程关键字volatile、lock、synchronized
--------------------- 本文来自 旭日Follow_24 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/xuri24/article/detail ...
随机推荐
- http报文和协议首部
http报文和协议首部 http报文 3>报文格式 request 报文 <method> <request-URL> <version> <heade ...
- E20170610-hm
presence n. 出席; 仪表; 风度; 鬼魂,神灵; defence n. 防御; 辩护; 防御工事; 后卫; phyle n. 种族,宗族; race n. 赛跑; 民族; 人种; ...
- bzoj 1671: [Usaco2005 Dec]Knights of Ni 骑士【bfs】
bfs预处理出每个点s和t的距离d1和d2(无法到达标为inf),然后在若干灌木丛格子(x,y)里取min(d1[x][y]+d2[x][y]) /* 0:贝茜可以通过的空地 1:由于各种原因而不可通 ...
- bzoj 1724: [Usaco2006 Nov]Fence Repair 切割木板【堆】
如果反着看,看成合并木板,就和合并果子一样了,把若干块放进一个小根堆,然后每次取出两个合并,把合并结果加进答案和堆里 代码里小根堆用优先队列实现(懒 #include<iostream> ...
- linux编译安装gcc5.3.0
1.下载GCC5.3.0安装包 #su #cd /opt #wget http://ftp.gnu.org/gnu/gcc/gcc-5.3.0/gcc-5.3.0.tar.gz 2.解压 #.tar. ...
- [GDOI2014]拯救莫莉斯
题目描述 莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场. 圣域的地图可以看成是一个n*m的矩阵.每个整数坐标点(x , y)表示一座城市吗,两座城市间相邻 ...
- 【SPOJ-GCDEX】GCD Extreme(欧拉函数)
题目: SPOJ-GCDEX (洛谷 Remote Judge) 分析: 求: \[\sum_{i=1}^{n}\sum_{j=i+1}^{n}gcd(i,j)\] 这道题给同届新生讲过,由于种种原因 ...
- ACM_01背包(恰好装满)
背包2 Time Limit: 2000/1000ms (Java/Others) Problem Description: 有n个重量和价值分别为Wi,Vi的物品,现从这些物品中挑选出总量刚好为 W ...
- NHibernate3.2学习笔记-几种查询方式
一.开发环境 数据库:SQLServer2008 编译器:VS2010 .Net版本:.Net Framework 4.0 二.开发过程 1.项目结构 承接上一篇 2.执行sql语句 (1)基本语法 ...
- Python操作远程数据库
我的项目要往数据库中插入create_time和update_time,那就势必要引用现在的系统时间,经过大量的查找,终于发现往python是没有对应时间datetime的相关通配符的,那么我们要怎么 ...