Java多线程~常见的锁策略(乐观锁与悲观锁、读写锁、自旋锁、公平锁与非公平锁、可重入锁与不可重入锁)

线程冲突: 多个线程并发并行的对同一个共享变量进行操作(存在线程安全问题)

乐观锁与悲观锁

乐观锁: 以乐观的心态来看待线程冲突,总是觉得不会有多个线程同时操作同一个共享变量,所以每次都不加锁,直接操作共享变量.

悲观锁:以悲观的心态来看待线程冲突,总是觉得会有其他线程同时操作共享变量,所以每次都加锁操作共享变量.

乐观锁适用于大部分情况下不存在线程冲突的场景,而悲观锁适用于大部分情况下存在线程冲突的场景.

读写锁

多个线程之间,读读操作不会产生线程安全问题,但读操作和写操作之间需要进行互斥,否则会产生线程安全问题,如果两种情况下都使用同一种锁,就会产生极大的性能消耗,因此出现了读写锁.

读写锁就是把读操作和写操作区分对待,java标准库中提供了ReentrantReadWriteLock类来实现了读写锁 

在读写锁的使用中:

· 读加锁和读加锁之间不互斥

· 读加锁和写加锁之间互斥

· 写加锁和写加锁之间互斥

使用示例

public class LockDemo {
    public static void main(String[] args) {
        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();//读锁
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();//写锁

        //写操作
        writeLock.lock();//写锁加锁
        //写
        writeLock.unlock();//写锁释放锁
        
        //读操作
        readLock.lock();//读锁加锁
        //读
        readLock.unlock();//读锁释放锁
    }
}

读写锁特别适合与“频繁读,不频繁写”的场景 

自旋锁

 自旋锁通常是结合CAS来实现的,以此来保证线程安全的修改变量操作.

通常情况下,线程在抢占锁失败后就进入了BLOCKED状态,放弃CPU,需要再过很久才可能被调度的.但是大部分情况下,虽然抢占锁失败,但过不了多久锁就会被释放,因此没有必要放弃CPU,这时就可以通过自旋锁来处理这样的问题

自旋锁的原理:如果获取锁失败,立即再次尝试获取,无限循环,直到获取到锁为止,一旦锁被其他线程释放,就能立刻获取到锁.

自旋锁的优缺点
优点:没有放弃CPU,不涉及线程的阻塞和调度,一旦锁被释放,就能第一时间获取到锁

缺点:如果锁被其他线程长时间占有,就会持续的消耗CPU资源

公平锁与非公平锁

公平锁:以申请锁时间的先后顺序来获取锁,类似于排队买票

优点:线程以申请锁的先后顺序来获取锁,不会出现争抢的情况,也不会出现线程饥饿.

缺点:效率较低,等待队列中除一个线程外,其他线程都会阻塞,CPU唤醒线程的开销比非公平锁大

非公平锁:以竞争的方式来获取锁

优点:不考虑执行顺序,效率高

缺点: 可能出现线程饥饿的现象

例如synchronized申请锁对象时,就是非公平锁.

可重入锁与不可重入锁

一个线程可以多次获取同一个锁就是可重入锁,反之就是不可重入锁

ReentrantLock和synchronized都是可重入锁

例如下列代码,就是可重入锁synchronized的示例

package threadhomework;

/**
 * Created with IntelliJ IDEA.
 * Description: 可重入锁示例
 * User: Li_yizYa
 * Date: 2022—09—23
 * Time: 22:51
 */
public class MyRunnable implements Runnable {
    public synchronized void set() {
        System.out.println(Thread.currentThread().getName());
        get();
    }
    public synchronized void get() {
        System.out.println(Thread.currentThread().getName());
    }

    @Override
    public void run() {
        set();
    }

    public static void main(String[] args) {
        MyRunnable r = new MyRunnable();
        while(true) {
            new Thread(r).start();
        }
    }
}

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Li_yizYa

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

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

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

打赏作者

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

抵扣说明:

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

余额充值