本地锁和分布式锁的区别

1. 本地锁和分布式锁的区别。

1.1. 本地锁的意义

在单进程的系统中,当存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行,以防止并发修改变量带来数据不一致或者数据污染的现象。
而为了实现多个线程在一个时刻同一个代码块只能有一个线程可执行,那么需要在某个地方做个标记,这个标记必须每个线程都能看到,当标记不存在时可以设置该标记,其余后续线程发现已经有标记了则等待拥有标记的线程结束同步代码块取消标记后再去尝试设置标记。这个标记可以理解为锁。

1.2. 分布式锁的意义

如果是单机情况下(单JVM),线程之间共享内存,只要使用线程锁就可以解决并发问题。但如果是分布式情况下(多JVM),线程A和线程B很可能不是在同一JVM中,这样线程锁就无法起到作用了,这时候就要用到分布式锁来解决。
分布式锁是控制分布式系统同步访问共享资源的一种方式。

2. 本地锁

2.1. 常用的本地锁有什么?

​ synchronized和lock

2.2. 本地锁锁的是什么?

​ 在非静态方法中,锁的是对象,在非静态方法中,锁的是类字节码

2.3. lock锁的原理?

通过查看公平锁的源码得知在java.util.concurrent.locks的抽象类中AbstractQueuedSynchronizer,存在一个int类型的状态值,然后进行判断这个锁的状态。
Java ReenttrantLock通过构造函数指定该锁是否公平,默认是非公平锁,因为非公平锁的优点在于吞吐量比公平锁大,对于synchronized而言,也是一种非公平锁

2.4. volatile是什么意思?干什么用的?
2.41 volatile定义

是java虚拟机提供的轻量级的同步机制。保证可见性,不保证原子性,禁止指令重排。

2.42 volatile的可见性

由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(也叫栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行。操作过程有三步。

首先要将变量从主内存拷贝到自己的工作内存空间。
然后对变量进行操作。
操作完成后再将变量写回主内存,不能直接操作主内存中的变量。

各个线程中的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下。
在这里插入图片描述

2.43 volatile的不保证原子性

​ 原子性定义:不可分割,完整性,也就是说某个线程正在做某个具体业务时,中间不可以被加塞或者被分割,需要具体完成,要么同时成功,要么同时失败。
在这里插入图片描述

2.44 如何让volatile保证原子性

最简单的方法,加sync的锁。
可以使用JUC下面的原子包装类。

2.45 volatile禁止指令重排

单线程环境里面确保最终执行结果和代码顺序的结果一致
处理器在进行重排序时,必须要考虑指令之间的数据依赖性
多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。

public void mySort() {
   
	int x = 11;
	int y = 12;
	x = x + 5;
	y = x * x;
}

按照正常单线程环境,执行顺序是 1 2 3 4
但是在多线程环境下,可能出现以下的顺序:

  • 2 1 3 4
  • 1 3 2 4

上述的过程就可以当做是指令的重排,即内部执行顺序,和我们的代码顺序不一样
但是指令重排也是有限制的,即不会出现下面的顺序

  • 4 3 2 1

因为处理器在进行重排时候,必须考虑到指令之间的数据依赖性
因为步骤 4:需要依赖于 y的申明,以及x的申明,故因为存在数据依赖,无法首先执行

2.5. synchronized原理

常用的使用方法:
java对象布局?整合对象一共16B,其中对象头有12B,还有4B是对齐的字节(64位虚拟机上对象的大小必须是8的倍数)
对象头里边存的是什么呢?
对象头就是所有对象开头的公共部分。

2.5. bit和byte的区别?
​ bit意为“位”或“比特”,是计算机运算的基础;
​ byte意为“字节”,是计算机文件大小的基本计算单位;
​ byte=字节即1byte=8bits,两者换算是1:8的关系。

3. 锁的类型:

  • 从线程是否需要对资源加锁可以分为 悲观锁 和 乐观锁。
  • 从锁的公平性进行区分,可以分为公平锁 和 非公平锁。
  • 从多个线程能否获取同一把锁分为 共享锁 和 排他锁
  • 从资源已被锁定,线程是否阻塞可以分为 自旋锁。
3.1. 悲观锁 和 乐观锁。
3.11 悲观锁

​ 悲观锁是一种悲观思想,它总认为最坏的情况可能会出现,它认为数据很可能会被其他人所修改,所以悲观锁在持有数据的时候总会把资源 或者 数据 锁住,这样其他线程想要请求这个资源的时候就会阻塞,直到等到悲观锁把资源释放为止。传统的关系型数据库里边就用到了很多这种锁机制,**比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。**悲观锁的实现往往依靠数据库本身的锁功能实现。
​ Java 中的 Synchronized 和 ReentrantLock 等独占锁(排他锁)也是一种悲观锁思想的实现,因为 Synchronzied 和 ReetrantLock 不管是否持有资源,它都会尝试去加锁,生怕自己心爱的宝贝被别人拿走。

3.12 乐观锁

​ 而乐观锁的思想与悲观锁的思想相反,它总认为资源和数据不会被别人所修改,所以读取不会上锁,但是乐观锁在进行写入操作的时候会判断当前数据是否被修改过(具体如何判断我们下面再说)。乐观锁的实现方案一般来说有两种:版本号机制 和 CAS实现 。乐观锁多适用于多读的应用类型,这样可以提高吞吐量。比如在MyBaits-Plus中是支持乐观锁机制的。

乐观锁实现方式:版本号机制,参考代码

3.2. 公平锁 和 非公平锁。
3.21 公平锁定义

​ 在并发环境中,多个线程需要对同一资源进行访问,同一时刻只能有一个线程能够获取到锁并进行资源访问,那么剩下的这些线程怎么办呢?这就好比食堂排队打饭的模型,最先到达食堂的人拥有最先买饭的权利,那么剩下的人就需要在第一个人后面排队,这是理想的情况,即每个人都能够买上饭。那么现实情况是,在你排队的过程中,就有个别不老实的人想走捷径,插队打饭,如果插队的这个人后面没有人制止他这种行为,他就能够顺利买上饭,如果有人制止,他就也得去队伍后面排队。
​ 根据以上总结,公平锁表示在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列中的第一个,就占用锁,否者就会加入到等待队列中,以后按照FIFO的规则从队列中取到自己
在这里插入图片描述

3.22 公平锁使用

​ 在 Java 中,我们一般通过 ReetrantLock 来实现锁的公平性。

public class MyFairLock extends Thread {
   
    //创建公平锁
    private ReentrantLock 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT界的一只菜鸟

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值