关于synchronized与volatile的一点认识
贪婪是一种原罪,不要再追求性能的路上离正确越来越远。
- 内存模型
- 锁synchronized
- 什么是锁
pageId=27903261#%E5%85%B3%E4%BA%8Esynchronized%E4%B8%8Evolatile%E7%9A%84%E4%B8%80%E7%82%B9%E8%AE%A4%E8%AF%86-%E7%8B%AC%E5%8D%A0%E9%94%81" style="color:rgb(0,109,175); text-decoration:none">独占锁
pageId=27903261#%E5%85%B3%E4%BA%8Esynchronized%E4%B8%8Evolatile%E7%9A%84%E4%B8%80%E7%82%B9%E8%AE%A4%E8%AF%86-%E5%88%86%E6%8B%86%E9%94%81" style="color:rgb(0,109,175); text-decoration:none">分拆锁
pageId=27903261#%E5%85%B3%E4%BA%8Esynchronized%E4%B8%8Evolatile%E7%9A%84%E4%B8%80%E7%82%B9%E8%AE%A4%E8%AF%86-%E5%88%86%E7%A6%BB%E9%94%81" style="color:rgb(0,109,175); text-decoration:none">分离锁
- 分布式锁
- volatile
内存模型
java内存模型
提到同步、锁。就必须提到java的内存模型,为了提高程序的运行效率。java也吸收了传统应用程序的多级缓存体系。
在共享内存的多处理器体系架构中,每一个处理器都拥有自己的缓存,而且定期地与主内存进行协调。在不同的处理器架构中提供了不同级别的缓存一致性(Cache Coherence),当中一部分仅仅提供最小的保证,即同意不同的处理器在随意时刻从同一个存储位置上看到不同的值。操作系统、编译器以及执行时(有时甚至包含应用程序)须要弥合这样的在硬件能力与线程安全之间的差异。
要想确保每一个处理器都能在随意时刻知道其它处理器正在进行的工作,将须要很大的开销。
在大多数时间里。这样的信息是不必要的。
因此处理器会适当放宽存储一致性保证,以换取性能的提升。在架构定义的内存模型中将告诉应用程序能够从内存系统中获得如何的保证。此外还定义了一些特殊的指令(称为内存栅栏),当须要共享数据时,这些指令就能实现额外的存储协调保证。为了使java开发者无须关心不同架构内存模型之间的差异,Java还提供了自己的内存模型。而且JVM通过在适当的位置上插入内存栅栏来屏蔽在JVM与底层之平台内存模型之间的差异。
经过上面的解说和上图,我们知道线程在执行时候有一块内存专用区域。Java程序会将变量同步到线程所在的内存。这时候会操作工作内存中的变量。而线程中的变量何时同步回到内存是不可预期的。可是java内存模型规定,通过关键词”synchronized“、”volatile“能够让java保证某些约束。
“volatile” - 保证读写的都是主内存变量。
“synchronized” - 保证在块開始时。都同步主内存值到工作内存,而快结束时。将工作内存同步会主内存。 |
重排序
public class PossibleReordering {
static int x = 0,y=0;
static int a=0,b=0;
public static void main(String[] args) throws InterruptedException {
Thread one = new Thread(new Runnable() { @Override
public void run() {
a = 1;
x = b;
}
}); Thread two = new Thread(new Runnable() { @Override
public void run() {
b = 2;
y = a;
}
});
one.start();two.start();
one.join();two.join();
System.out.println("x:" + x+",y:"+y);
}
}
重排序。如上图。运行结果,一般人可能觉得是1,1;真正的运行结果可能每次都不一样。拜JMM重排序所赐。JMM使得不同线程的操作顺序是不同的。从而导致在缺乏同步的情况下,要判断操作的运行结果将变得更加复杂。各种使操作延迟或看似乱序运行的不同原因,都能够归为重排序。内存级的重排序会使程序的行为变得不可预測。
假设没有同步,要判断出程序的运行顺序是很困难的。而要确保在程序中正确的使用同步却是很easy的。
同步将限制编译器和硬件运行时对内存操作重排序的方式。
锁synchronized
锁实现了对临界资源的相互排斥訪问,被synchronized修饰的代码仅仅有一条线程能够通过,是严格的排它锁、相互排斥锁。
没有获得相应锁对象监视器(monitor)的线程会进入等待队列。不论什么线程必须获得monitor的全部权才干够进入同步块,退出同步快或者遇到异常都要释放全部权,JVM规范通过两个内存屏障(memory barrier)命令来实现排它逻辑。内存屏障能够理解成顺序运行的一组CPU指令,全然无视指令重排序。
什么是锁
public class TestStatic {
public syncronized static void write(boolean flag) {
xxxxx
}
public synchronized static void read() {
xxxxx
}
}
线程1訪问TestStatic.write()方法时,线程2能訪问TestStatic.read()方法吗
线程1訪问new TestStatic().write()方法时,线程2能訪问new TestStatic().read()方法吗
线程1訪问TestStatic.write()方法时。线程2能訪问new TestStatic().read()方法吗
public class Test {
public syncronized void write(boolean flag) {
xxxxx
}
public synchronized void read() {
xxxxx
}
}
Test test = new Test();线程1訪问test.write() 方法。线程2是否能訪问test.read()方法
Test a = new Test(); Test b = new Test();线程1訪问a.write()訪问,线程2是否能訪问b.read()方法
答案。java中每一个对象都能够作为一个锁,而对象就决定了锁的粒度大小。
对于实例同步方法,锁是当前对象。
对于静态方法。锁是TestSTatic.class对象
对于同步代码块。锁是Synchronized括号中面配置的对象
TestStatic类,1问,作用范围全体class对象。线程1拿到。线程2就不能拿到
2问,3问同上
Test类。1问,不能,锁都是实例对象test,线程1拿到锁之后,线程2无法訪问
2问,能够,线程1锁是实例a,线程2是实例b。
独占锁
假设你不敢确定该用什么锁,就用这个吧,在保证正确的前提下,兴许在提高开发效率。
public class ServerStatus {
public final Set<String> users;
public final Set<String> quers;
public synchronized void addUser(String u ) {
users.add(u);
}
public synchronized void addQuery(String q ) {
quers.add(q);
}
public synchronized void removeUser(String u) {
users.remove(u);
}
public synchronized void removeQuery(String q) {
quers.remove(q);
}
}
分拆锁
假设在整个应用程序仅仅有一个锁,而不是为每一个对象分配一个独立的锁,那么全部同步代码块的运行就会变成串行化运行。因为非常多线程都会竞争同一个全局锁,因此两个线程同一时候请求这个锁的概率将会剧增,从而导致更严重的竞争。
所以假设将这些锁请求分到很多其它的锁上,就能有效减少锁竞争程度。因为等待而被堵塞的线程将更少。从而可伸缩性将提高。
上文中users、quers是两个相互独立的变量,能够将此分解为两个独立的锁,每一个锁仅仅保护一个变量。减少每一个锁被请求的频率。
public class ServerStatus {
public final Set<String> users;
public final Set<String> quers;
public void addUser(String u ) {
synchronized(users) {
users.add(u);
}
}
public void addQuery(String q ) {
synchronized(quers) {
quers.add(q);
}
}
public void removeUser(String u) {
synchronized(users) {
users.remove(u);
}
}
public void removeQuery(String q) {
synchronized(quers) {
quers.remove(q);
}
}
}
分离锁
在某些情况下,能够将锁分解技术进一步扩展为对一组独立对象上的锁进行分解。这样的情况称为锁分段。
比如ConcurrencyHashMap是有一个包括16个锁的数组实现,每一个锁保护全部散列桶的1/16,当中第N个散列桶由第(N mod 16)个锁来保护。如果全部keyword都时间均与分布,那么相当于把锁的请求降低到原来的1/16,能够支持多达16个的并发写入。
锁分段的劣势在于:与採用单个锁来实现独占訪问相比,要获取多个锁来实现独占訪问将更加困难而且开销更高,比方计算size、重hash。
分布式锁
zookeeper。推断暂时节点是否存在,存在就说明已经有人争抢到锁;不存在就创建节点,表明拥有该锁。
记下,以后具体研究
https://github.com/qhwj2006/ZookeeperDistributedLock
https://github.com/s7/scale7-cages
volatile
volatile是比synchronized更轻量级的同步原语。volatile能够修饰实例变量、静态变量、以及数组变量(网上大牛说。维护的是引用,可是里面的对象。。
。
嘿嘿嘿)。
被volatile修饰的变量。JVM规范规定,一个线程在改动完,另外的线程能读取最新的值。
但只保证可见性,不保证原子性。所以volatile通经常使用来修饰boolean类型或者状态比較少的数据类型,并且不能用来更新依赖变量之前值的操作(例volatile++)。
volatile内部不过对变量的操作多了一条cpu指令(lock#指令),它会强制写数据到缓存,假设缓存数据同一时候也在主存。会强制写数据更新到主存,而且使全部持有该主存数据地址的缓存统统失效,触发其它持有缓存数据的线程从主存获取最新数据,从而实现同步。
关于synchronized与volatile的一点认识的更多相关文章
- 4个点说清楚Java中synchronized和volatile的区别
作者 : Hollis 回顾一下两个关键字:synchronized和volatile 1.Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如sy ...
- 多线程学习三:Thread API,ThreadLocal,synchronized,volatile和Condition
一.Thread API: setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 首先要了解什么是Thread. ...
- Java 线程 — synchronized、volatile、锁
线程同步基础 synchronized 和volatile是Java线程同步的基础. synchronized 将临界区的内容上锁,同一时刻只有一个进程能访问该临界区代码 使用的是内置锁,锁一个时刻只 ...
- synchronized和volatile的使用
synchronized和volatile的使用 一步一步掌握线程机制(三)---synchronized和volatile的使用 现在开始进入线程编程中最重要的话题---数据同步,它是线程编程的核心 ...
- java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析
java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...
- java多线程之内存可见性-synchronized、volatile
1.JMM:Java Memory Model(Java内存模型) 关于synchronized的两条规定: 1.线程解锁前,必须把共享变量的最新值刷新到主内存中 2.线程加锁时,将清空工作内存中共享 ...
- synchronized和volatile比较
synchronized和volatile比较 volatile不需要加锁,比synchronized更轻量级,不会阻塞线程 从内存可见性角度讲,volatile读相当于加锁,volatile写相当于 ...
- java并发编程(2) --Synchronized与Volatile区别
Synchronized 在多线程并发中synchronized一直是元老级别的角色.利用synchronized来实现同步具体有一下三种表现形式: 对于普通的同步方法,锁是当前实例对象. 对于静态同 ...
- synchronized与volatile的区别及各自的作用、原理(学习记录)
synchronized与volatile的区别,它们的作用及原理? 说到两者的区别,先要了解锁提供的两种特性:互斥(mutual exclusion) 和可见性(visibility). 互斥:即一 ...
随机推荐
- putty配色方案【转】
本文转载自:http://blog.csdn.net/hfut_jf/article/details/53636080 putty默认的配色方案简直毫无人道主义可言,所以找了个,好多了,转载自http ...
- 设备树学习之(一)GPIO中断【转】
本文转载自:http://blog.csdn.net/lizuobin2/article/details/54563587 开发板:tiny4412SDK + S702 + 4GB Flash 要移植 ...
- 【BZOJ3926】【ZJOI2015】诸神眷顾的幻想乡 广义后缀自动机
题目: 题目在这里 思路&做法: 参考的题解 既然只有\(20\)个叶子节点, 那么可以从每个叶子节点往上建一颗\(trie\)树, 然后合并成一棵大的\(trie\)树, 然后构建广义后缀自 ...
- Excel 查找某列中的数据在另一列是否存在并输出其他列的数据
最近在操作Excel文件数据导入数据库时,经常需要检查Excel中哪些数据数据库中已经存在,哪些不存在,然后再将不存在数据库中的Excel数据导入:在此过程中,经常需要操作Excel中的数据,所以.也 ...
- python基本数据类型之列表list
list的基本功能 结果类型 中括号括起来 逗号(,)分割每一个元素 列表中的元素可以是数字,字符串,列表,布尔值所有的都能放 索引,切片 ? 1 2 3 li = [1, 3, 5, " ...
- 学习java的方式
- JS 经验总结
1.IE中div的高度是content+padding+border之和,其它的是content的高度 2.一个标签里面只有一个属性,class='cls1 cls2' 3.同一页面不能出现相同的id ...
- 依赖注入与Service Locator
为什么需要依赖注入? ServiceUser是组件,在编写者之外的环境内被使用,且使用者不能改变其源代码. ServiceProvider是服务,其类似于ServiceUser,都要被其他应用使用,不 ...
- axis2 1.7.1使用教程
写在前面 本文只说Axis2的用法. 1.下载与部署 需要下载两个文件: 下载地址:http://mirrors.cnnic.cn/apache/axis/axis2/java/core/1.7.1/ ...
- Arduino UNO R3
Arduino 常见型号 当然还有 LilyPad,附图: 最常见的自然是UNO,最新版是第三版R3: 国内也有一些改进的板子.我用的是一般的板子,拿到货也只能默默了. 简介 The Uno is a ...