一、java内存模型

提到同步、锁,就必须提到Java的内存模型,为了提高程序的执行效率,java也吸收了传统应用程序的多级缓存体系。

在共享内存的多处理器体系架构中,每个处理器都拥有自己的缓存,并且定期地与主内存进行协调。在不同的处理器架构中提供了不同级别的缓存一致性(Cache Coherence),其中一部分只提供最小的保证,即允许不同的处理器在任意时刻从同一个存储位置上看到不同的值。操作系统、编译器以及运行时(有时甚至包括应用程序)需要弥合这种在硬件能力与线程安全之间的差异。

要想确保每个处理器都能在任意时刻知道其他处理器正在进行的工作,将需要非常大的开销。在大多数时间里,这种信息是不必要的。因此处理器会适当放宽存储一致性保证,以换取性能的提升。在架构定义的内存模型中将告诉应用程序可以从内存系统中获得怎样的保证,此外还定义了一些特殊的指令(称为内存栅栏),当需要共享数据时,这些指令就能实现额外的存储协调保证。为了使java开发人员无须关心不同架构内存模型之间的差异,Java还提供了自己的内存模型,并且JVM通过在适当的位置上插入内存栅栏来屏蔽在JVM与底层之平台内存模型之间的差异。

经过上面的讲解和上图,我们知道线程在运行时候有一块内存专用区域,Java程序会将变量同步到线程所在的内存。这时候会操作工作内存中的变量,而线程中的变量何时同步回到内存是不可预期的。但是java内存模型规定,通过关键词”synchronized“、”volatile“可以让java保证某些约束。

“volatile” - 保证读写的都是主内存变量。

“synchronized” - 保证在块开始时,都同步主内存值到工作内存,而快结束时,将工作内存同步会主内存。

重排序

public classPossibleReordering {static int x = 0,y=0;static int a=0,b=0;public static void main(String[] args) throwsInterruptedException {

Thread one= new Thread(newRunnable() {

@Overridepublic voidrun() {

a= 1;

x=b;

}

});

Thread two= new Thread(newRunnable() {

@Overridepublic voidrun() {

b= 2;

y=a;

}

});

one.start();two.start();

one.join();two.join();

System.out.println("x:" + x+",y:"+y);

}

}

重排序。如上图,执行结果,一般人可能认为是1,1;真正的执行结果可能每次都不一样。拜JMM重排序所赐,JMM使得不同线程的操作顺序是不同的,从而导致在缺乏同步的情况下,要推断操作的执行结果将变得更加复杂。各种使操作延迟或看似乱序执行的不同原因,都可以归为重排序。内存级的重排序会使程序的行为变得不可预测。如果没有同步,要推断出程序的执行顺序是非常困难的,而要确保在程序中正确的使用同步却是非常容易的。同步将限制编译器和硬件运行时对内存操作重排序的方式。

锁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 users;

public final Set 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 users;

public final Set 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)个锁来保护。假设所有关键字都时间均与分布,那么相当于把锁的请求减少到原来的1/16,可以支持多达16个的并发写入。

锁分段的劣势在于:与采用单个锁来实现独占访问相比,要获取多个锁来实现独占访问将更加困难并且开销更高,比如计算size、重hash。

分布式锁

zookeeper,判断临时节点是否存在,存在就说明已经有人争抢到锁;不存在就创建节点,表明拥有该锁。

记下,以后详细研究

《分布式锁实现:数据库、redis、zookeeper》

volatile

volatile是比synchronized更轻量级的同步原语,volatile可以修饰实例变量、静态变量、以及数组变量(网上大牛说,维护的是引用,但是里面的对象。。。嘿嘿嘿)。被volatile修饰的变量,JVM规范规定,一个线程在修改完,另外的线程能读取最新的值。

但仅仅保证可见性,不保证原子性,所以volatile通常用来修饰boolean类型或者状态比较少的数据类型,而且不能用来更新依赖变量之前值的操作(例volatile++)。

volatile内部仅仅是对变量的操作多了一条cpu指令(lock#指令),它会强制写数据到缓存,如果缓存数据同时也在主存,会强制写数据更新到主存,并且使所有持有该主存数据地址的缓存统统失效,触发其他持有缓存数据的线程从主存获取最新数据,从而实现同步。

java 独占锁_锁分类(独占锁、分拆锁、分离锁、分布式锁)相关推荐

  1. redis分布式锁 在集群模式下如何实现_收藏慢慢看系列:简洁实用的Redis分布式锁用法...

    在微服务中很多情况下需要使用到分布式锁功能,而目前比较常见的方案是通过Redis来实现分布式锁,网上关于分布式锁的实现方式有很多,早期主要是基于Redisson等客户端,但在Spring Boot2. ...

  2. redis实现轮询算法_【07期】Redis中是如何实现分布式锁的?

    点击上方"Java面试题精选",关注公众号 面试刷图,查缺补漏 分布式锁常见的三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁. 本地面 ...

  3. 苹果手机怎么解屏幕锁_小米手机刷机第1步:解BL锁教程

    本文首发我的微信公众号:酷客fans 玩机教程,ROM资源,主题推荐,原生进阶指南,教你如何玩转安卓,欢迎关注. 第一步:解锁 现在很多手机都有一个BL锁,其主要目的是为了用户的隐私数据安全.但是对于 ...

  4. 《大厂高并发分布式锁从入门到实战》第3讲之Redisson分布式锁

    目录 一.Redisson介绍 二.Redisson中的几种锁 大多数生产环境中,一台redis节点肯定不能满足业务需要,通常会采用多节点redis集群部署,但是redis集群部署条件下可能会产生锁失 ...

  5. java币值转换_-PAT-java-5-23 币值转换 (20分)

    输入一个整数(位数不超过9位)代表一个人民币值(单位为元),请转换成财务要求的大写中文格式.如23108元,转换后变成"贰万叁仟壹百零捌"元.为了简化输出,用小写英文字母a-j顺序 ...

  6. java 答题卡_试题六(共15分) 阅读下列说明和Java代码,将应填入(n)处的字句写在答题纸的对应栏内。【说明】某咖啡 - 赏学吧...

    试题六(共15分) 阅读下列说明和Java代码,将应填入(n)处的字句写在答题纸的对应栏内. [说明] 某咖啡店当卖咖啡时,可以根据顾客的要求在其中加入各种配料,咖啡店会根据所加入的配料来计算费用.咖 ...

  7. java 答题卡_试题八(共15分)阅读以下说明和Java程序代码,将应填入(n) 处的字句写在答题纸的对应栏内。[说明]在 - 赏学吧...

    试题八(共15分) 阅读以下说明和Java程序代码,将应填入(n) 处的字句写在答题纸的对应栏内. [说明] 在下面的 Java 程序代码中,类SalesTicket 能够完成打印票据正文的功能,类H ...

  8. java 仓库类_仓库类型和功能分别是什么?

    [判断题]差动放大器的两只三极管的性能参数必须完全一致 [判断题]蕨类植物是低等植物吗? [多选题]中国中国宗法制度包括:( ) [单选题]中国古代的宗法制产生于( ) [判断题]当运放单电源运用时, ...

  9. java如何保证redis设置过期时间的原子性_分布式锁用 Redis 还是 Zookeeper

    在讨论这个问题之前,我们先来看一个业务场景: 系统A是一个电商系统,目前是一台机器部署,系统中有一个用户下订单的接口,但是用户下订单之前一定要去检查一下库存,确保库存足够了才会给用户下单. 由于系统有 ...

  10. java 通过redis实现倒计时_突破Java面试(42) - Redis amp; ZooKeeper两种分布式锁实现的优劣...

    0 Github 1 面试题 一般实现分布式锁都有哪些方式?使用redis如何设计分布式锁?使用zk来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高? 2 考点分析 一般先问问你zk,然后 ...

最新文章

  1. c 获取mysql列数据_转 用C API 操作MySQL数据库
  2. 查询当天数据_【财会人职场必备】发票勾选、查询、认证等25问!简直太全了!都收藏了!...
  3. 戴尔硬盘保护增强套件_拆解戴尔服务器,看看内部构造与普通计算机的区别
  4. location驱动包_Zynq SDK 驱动探求(三):论一个外设驱动的全部身家·Xilinx SDK 驱动源码结构...
  5. node-vue前后端分离记录
  6. 大疆Onboard SDK开发中连接飞控后串口设置与开机自启动
  7. Theano 中文文档 0.9 - 7.2.3 Theano中的导数
  8. 各大搜索引擎提交地址
  9. python找第二大的数索引_python – 在numpy数组中查找多个值的行索引
  10. day11【过渡】SpringBoot
  11. mysql varchar varbinary_mysql varbinary vs varchar
  12. 如何卸载 think-cell?丨卸载教程丨卸载办法
  13. JavaScript循环刷新页面
  14. 发布源码及依赖到网络maven仓库
  15. 达摩院量子计算机叫什么,刚刚,阿里巴巴达摩院宣布研制出全球最强量子电路模拟器...
  16. Unity TrailRenderer实现拖尾
  17. 限制性定语从句和非限制性定语从句的四大区别
  18. 多组输入与单组输入的区分
  19. js调用vlc_web网页中使用vlc插件播放相机rtsp流视频
  20. 【Python】90后的青春,定格在被淡忘的QQ空间里

热门文章

  1. 深入mysql慢查询设置的详解
  2. Linux 每日一练习!!反单引号·(键盘上数字1左边)··
  3. jQuery缓存数据——仿Map
  4. [Mac]一些命令技巧
  5. 演示: GTS流量×××和CAR流量监管的效果及相关实践计划
  6. mysql开启慢查询方法(转)
  7. [Unity3d]多个摄像机叠加效果
  8. 如何在Win Server 2008R2环境下,把域帐户加到本地管理员组??
  9. HDU 2063 过山车【二分图最大匹配】
  10. 学习笔记(14):Python网络编程并发编程-文件传输功能实现