4/11/2024 lock

# 互斥锁

# 1.显示锁 ReentrantLock (可重入锁)

# 特性

支持公平锁、非公平锁

  • 公平锁 线程请求来了,只能在后面排队
  • 非公平锁 线程要抢占cpu去处理

持有锁期间,可以被中断

支持主动放弃,tryLock

JDK支持,使用稍复杂,必须在finally中unLock()

指当前线程获取==同一个锁==时不会出现死锁。意味着当前线程获取相同的锁时再去获取锁不会被阻塞

# 2.内置锁 synchronized

# 特性

仅支持非公平锁

持有锁期间,不可中断

不支持主动放弃

Java内置,使用简单

锁的是类实例或者类对象

通过monitorenter与monitorexit指令保证原子性

总结:

互斥锁的使用 最为重要的是关注锁,临界区,资源的关系。

可见性问题一定要用HB规则推断

转账案例

A通过上述代码转账给B,会出现线程不安全问题

如果把synchronized锁的粒度大一点的话就可以解决 上述图锁的是Accout.class

把临界区里面的共享变量balance锁住,才不会有问题

只有所有的共享变量共用一个锁,才能保证共享变量的读写不出现问题


# 死锁

# 线程饥饿死锁

# 如何避免死锁 Coffman条件

# 1.互斥:共享资源X和Y只能有一个被占用

方案:没有办法解决

# 2.占有且等待:线程T1已经取得共享资源X,在等待共享资源Y的时候,不释放共享资源X

方案:一次性申请所有资源

# 3.不可抢占:其他线程不能强行抢占线程T1占有的资源。

方案:主动放弃

实现:

如果tar.lock.tryLock()执行不成功,则主动放弃。

# 4.循环等待:线程T1等待线程T2占有的资源,线程T2等待线程T1占有的资源。

方案:资源排序

实现:A->B->C->A改为A->B->C

先申请A的锁,再申请B的锁,最后申请C的锁


# 管程

案例:

排队看病

科室1看完去科室2,科室2看完去科室3,科室3看完回科室1拿结果

给科室1建立一个病人队列,同时科室1只能容纳一个人在占用。科室2、3同理

并发条件下,同一时间的共享变量,只能由一个线程占有,其他线程只能等待(thread.wait())

代码:

static class monitor
{
    private Item buffer[] = new Item [N] ;
    private int count = 0;
    
    public synchronized void insert (Item item)
    {
        .....
    }
        //每次只能有一个线程进入insert函数,如果多个线程同时调用insert函数,则后来者需要排队等待
}
1
2
3
4
5
6
7
8
9
10
11

# MySQL锁

# 乐观锁

当前线程每次去操作数据的时候都认为别人不会修改,更新的时候会判断别人是否会去更新数据,通过版本来判断,如果数据被修改了就拒绝更新,例如cas是乐

观锁,但是严格来说并不是锁,通过原子性来保证数据的同步,

# 版本号方式

通过在数据表中添加一个版本号字段,每次修改操作都会更新版本号的值。在提交修改时,检查当前读取的版本号与修改前读取的版本号是否一致,如果一致则说

明没有其他用户修改数据,可以正常提交修改;如果不一致,则说明有其他用户修改了数据,需要回滚事务或重新尝试。

# 时间戳方式

通过在数据表中添加一个时间戳字段,记录数据的最后修改时间。在提交修改时,检查当前读取的时间戳与修改前读取的时间戳是否一致,如果一致则说明没有其

他用户修改数据,可以正常提交修改;如果不一致,则说明有其他用户修改了数据,需要回滚事务或重新尝试。

# 悲观锁

当前线程去操作数据的时候,总是认为别的线程会去修改数据,所以每次操作数据的时候都会上锁,别的线程去操作数据的时候就会阻塞,比如synchronized


# Redis 分布式锁

# jedis原生

Last Updated: 7/24/2025, 4:36:33 PM