7-3 锁与同步

一、同步

要读写共享的数据。

通过锁的机制,某个线程可以获得对数据的独家访问权,其他的线程则被阻塞,无法访问。

线程使用acquire获得锁,release释放。同一时刻一个锁只能被一个线程所有,其他的线程如果要申请只能等正在使用的线程去释放。

二、锁

每个对象都可以作为锁。java中使用synchronized来对某个语句上锁,表示某一时刻只有一个线程可以执行这条语句,执行完自动释放锁:

在这里插入图片描述

1、同步语句、代码块

注意,如果要防止竞争,要用同一把锁(相同的对象)进行保护。

例子,下图中所有的lock都是同一把锁,所以不同的线程之间不会有交错:

在这里插入图片描述

但如果像这样,就每个线程的锁都不一样的,起不到保护的作用:

在这里插入图片描述

Monitor模式:用ADT作为锁,把所有对ADT的访问都用同一把锁(ADT)锁起来:

在这里插入图片描述

2、同步方法

在方法前加上synchronized,相当于对同一个对象的每个方法加了相同的锁(当前对象):

在这里插入图片描述

由于锁的机制,线程A对某ADT的一个操作和线程B对相同ADT的一个操作会建立happen-before关系,即A的操作必然在B之前完全结束。

下图是一个错误使用锁的例子,由于每个线程的实例都不一样,所以这里每次循环用的都是不同的锁:

在这里插入图片描述

像这样每次循环用的就都是同一把锁(因为是同一个对象):

在这里插入图片描述

静态同步方法与类关联,这样同一个类的不同对象的锁也相同:

在这里插入图片描述

可见,使用静态同步方法可以非常好的保证线程安全,但是对性能的损耗也很大,最好确定哪些地方需要lock再有针对性地进行lock。

例子,sharedList是一个synchronizedList,下面对其的哪些操作应该加锁?调用isEmpty、add不需要,因为这俩都是原子操作;遍历需要加锁,防止遍历过程中有其他的操作对其进行更改;最后一个需要加锁,防止isEmpty和remove操作之间有另一个线程进行remove的操作:

在这里插入图片描述

例子,1和5是对的,其他都是错的,list作为锁被占用,但list仍然是可以被访问、修改的(3、4)。

在这里插入图片描述

总结:任何对mutable对象的操作(读/写)都应该被锁住;涉及对多个mutable对象的操作时,注意要用同一把锁进行保护。

三、原子性

加入我们通过monitor模式,已经实现了buf的toString、delete、insert方法的原子性,但将这些方法一起调用的时候还有可能会出现问题,会出现方法与方法之间的交错。比如说线程1delete一个字符串的同时,线程2delete相同的字符串,就会出错。

解决方法是如下图所示对代码块进行加锁,使用的锁为被访问的对象(下图的buf),这样对同一对象的访问会加锁、对不同对象的访问不会加锁:

在这里插入图片描述

四、死锁

当一个线程请求多把锁的时候,可能会出现死锁的情况。

下图会出现死锁的情况,线程A占用harry请求snape,线程B占用snape请求harry:

在这里插入图片描述
在这里插入图片描述

总的来说,如果出现了这样的形式,就有可能出现死锁:

在这里插入图片描述在这里插入图片描述

五、wait、notify、notifyAll

锁调用wait,wait可以让占用此锁的线程进入等待状态。notify可以随机唤醒一个正在wait的线程,notifyAll唤醒所有在wait的线程。

被唤醒的线程会继续请求锁,获取锁后,从wait的位置继续执行。

例如下图的线程使用wait,就会释放锁并且暂停执行:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值