synchronized

java课上讲到过synchronized

首先看看用synchronized和没用synchronized的区别

import lombok.Getter;
/**
* @author yintianhao
* @createTime 20190123 16:28
* @description synchronized
*/
public class SYN {
@Getter
private int count;
private Object lock;
public SYN(){
count = 0;
lock = new Object();
}
public void noSyn(){
for (int i = 0;i < 100;i++){
count++;
}
}
public void Syn(){
synchronized (lock){
for (int i = 0;i < 100;i++){
count++;
}
}
}
}

要进行的操作是对count进行自增操作,执行一次里面的Syn和noSyn函数都是对count这个变量进行加一百次的操作

不同的就是,Syn函数用了synchronized

然后开启十个线程来执行这两个函数,之后打印出count的值,我们期望的是count等于1000

List<Thread> threads = new ArrayList<>();
SYN syn = new SYN();
for (int i = 0;i < 10;i++){
threads.add(new Thread(()->syn.noSyn(),"thread-"+i));
}
for (Thread o:threads)
o.start();
for (Thread o:threads)
o.join();
System.out.println(syn.getCount());

再看运行结果

可是结果却不是1000

然后再把运行的函数改成Syn(),再次看结果

1000符合预期,为了避免偶然,重复运行几次结果仍是1000

那么为什么这里结果不同的呢,原因就是牵涉到另外一个概念,锁,在这个例子中,SYN类中的lock对象就可以看做一把锁

只有拿到锁,才能执行synchronized代码块中的代码块,所以当一个线程在执行synchronized里面的代码并且还没把锁交出来(释放)时,其他线程无法获取

到锁,故无法执行其中的代码,在没有使用synchronized的函数中,因为没有锁机制,有可能for循环还没结束的时候就有新的进程进来,即没有加到100就进来

故count最后不会到1000,而且也不是一个固定的数字

synchronized的实现原理

synchronized有三种用法:

第一种是对于同步方法,即修饰某个函数,这个时候锁对象是当前类的实例化对象

第二种是对于同步静态方法,这个时候锁是当前类的Class对象

第三种就是我上面的用法,对于同步代码块,锁是括号里的对象

前面说一个进程会获得锁,那么这个锁是存在在什么地方呢

首先需要了解java对象头,对象头分为两种

普通对象的对象头占两个字宽

数组对象的对象头占三个字宽,相比于普通对象的对象头多了一个字宽来存储数组长度

java对象头中的Markword字段中存储对象的HashCode,分代年龄和锁标记位,在运行期间,MarkWord存储的数据会随着锁标志位

的变化为变化,MarkWord的结构是这个样子的

这个锁的标志位就是锁的类型了,也就是之前所说的"锁"

Volatile

java对volatile的定义是:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量,

如果一个字段被申明为volatile,线程内存模型确保所有线程能够看到的这个变量的值是一致的

那么volatile是怎么保证可见性的呢

我们知道处理器为了保证处理的速度,是不和系统内存直接通信的,因为内存的速度远远慢yu每个处理器先是将系统内存中的数据读入到CPU内部缓存中

再进行操作,但是操作完成之后是不知道什么时候缓存中的数据会写到内存中的,那么其他处理器读取这个变量的时候就有可能读取到旧的数据,volatile是

这样子做的,一旦被volatile修饰的变量发生读写操作,就会发生两个动作:

1,将当前处理器缓存行的数据写到系统内存中(这是Lock前缀指令会引起的)

2,然后这个操作使得其他CPU缓存了该内存地址的数据无效

然后我们知道的是,每个处理器中的缓存是一致的,那么这个时候就需要缓存一致性协议,每个处理器嗅探总线上传播的数据来检测自己缓存中的值是否过期,

当发现自己的缓存行对应的内存地址被修改,就会将自己缓存行设置为无效,当需要修改的时候,重新从系统内存读取到缓存行,这样就实现了可见性

关于volatile变量的原子性,单个volatile变量的读写是具有原子性的,但是如果是类似于上面代码中的++这种复合操作,则不具有原子性

volatile和synchronized关键字的更多相关文章

  1. 从JAVA看C#中volatile和synchronized关键字的作用

    最近一直在想C#中 volatile关键字到底是用来干什么的?查了很多.NET的文章都是说用volatile修饰的变量可以让多线程同时修改,这是什么鬼... 然后查到了下面这篇JAVA中关于volat ...

  2. volatile与synchronized关键字

    volatile关键字相信了解Java多线程的读者都很清楚它的作用.volatile关键字用于声明简单类型变量,如int.float.boolean等数据类型.如果这些简单数据类型声明为volatil ...

  3. 《Java基础知识》Java锁详解(volatile,synchronized等)

    volatile: 让变量每次在使用的时候,都从主存中取. volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执 ...

  4. java中的volatile和synchronized

    关于volatile和同步相关的东西,网上有太多错误和解释不清的东西, 所以查阅相关书籍和文章后总结如下, 如果还是也存在不正确的内容,请一定要指出来, 以免误人子弟:) 1. 原子性与可视性 原子性 ...

  5. volatile和synchronized到底啥区别?多图文讲解告诉你

    你有一个思想,我有一个思想,我们交换后,一个人就有两个思想 If you can NOT explain it simply, you do NOT understand it well enough ...

  6. volatile关键字和synchronized关键字

    volatile关键字: 可以用来修饰字段(成员变量),就是告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性. synchron ...

  7. 并发编程之ThreadLocal、Volatile、synchronized、Atomic关键字扫盲

    前言 对于ThreadLocal.Volatile.synchronized.Atomic这四个关键字,我想一提及到大家肯定都想到的是解决在多线程并发环境下资源的共享问题,但是要细说每一个的特点.区别 ...

  8. 并发系列2:Java并发的基石,volatile关键字、synchronized关键字、乐观锁CAS操作

    由并发大师Doug Lea操刀的并发包Concurrent是并发编程的重要包,而并发包的基石又是volatile关键字.synchronized关键字.乐观锁CAS操作这些基础.因此了解他们的原理对我 ...

  9. 多线程的指令重排问题:as-if-serial语义,happens-before语义;volatile关键字,volatile和synchronized的区别

    一.指令重排问题 你写的代码有可能,根本没有按照你期望的顺序执行,因为编译器和 CPU 会尝试指令重排来让代码运行更高效,这就是指令重排. 1.1 虚拟机层面 我们都知道CPU执行指令的时候,访问内存 ...

随机推荐

  1. 浏览器中的 JS 和 Node.js 中的 JS

    一个是前端技术,一个是后端技术 浏览器中的 JavaScript ECMAScript  语言基础,如语法.数据类型结构.一些内置对象 BOM(Browser Object Model)  一些操作页 ...

  2. Sublime Text Snippets(代码片段)功能

    原文链接:http://www.bluesdream.com/blog/sublime-text-snippets-function.html 我们在编写代码的时候,总会遇到一些需要反复使用的代码片段 ...

  3. SystemUI中设置横竖屏显示

    SystemUI中快捷菜单有 “方向锁定” . RotationLockTile protected void handleClick() { if (mController == null) ret ...

  4. EL和JSTL笔记

    1.EL表达式 [1] 简介 > JSP表达式 <%= %> 用于向页面中输出一个对象. > 到JSP2.0时,在我们的页面中不允许出现 JSP表达式和 脚本片段. > ...

  5. AtCoder Regular Contest 090

    C - Candies 链接:https://arc090.contest.atcoder.jp/tasks/arc090_a 题意:从左上角走到右下角,只能向右和向下走,问能最多能拿多少糖果. 思路 ...

  6. redis学习-string常用命令

    keys * :查询所有的key值 set:为指定键设置对应的值 get:获取指定键的值 mset:一次传入多个键值对 mget:一次获取多个键的值 del:删除指定键 strlen:获取指定键值的长 ...

  7. Win7 VS2017 NASM编译FFMPEG(2018.12.22)

    今天无意中在gayhub发现个牛逼工程,全VS工程编译FFMPEG库,包括依赖库全是VS生成的,无需Mingw等Linux环境. 简单记录下过程,以防将来重装系统等情况,备忘. https://git ...

  8. maven 不能使用 snapshot 的解决方式

    最近项目需要用到snapshot的包来进行构建过程,但是怎么都下不了构建的snapshot包.查询了相关资料,发现网上的资料不全,特总结下: 我使用的是nexus来作为代理中央库proxy. 检查步骤 ...

  9. Document.write和 getElementById(ID)

    在javascript中,document.write()方法:常用来网页向文档中输出内容. 示例:通过document.write()方法,向网页文档中输出了一段文字. document.write ...

  10. PC装MAC(VM虚拟机)想体验苹果系统的福利

    Windows下 VM12虚拟机安装OS X 10.11(详细教程) 工具/原料 Mac OS X 10.11 镜像文件 unlocker208文件 VMware Workstation12(版本不一 ...