Java中锁的分类

        Java中很多锁的名词,并不是全指锁,有的是指锁的特性,有的是指锁的设计,有的是指锁的状态,下面总结的内容是对每个锁的名词进行一定的解释。

乐观锁/悲观锁

        乐观锁和悲观锁不是指具体的什么类型的锁,而是指看待并发的角度。

        乐观锁其实就是不加锁,乐观认为不加锁的并发操作是没有问题的。它是无锁编程,常常采用CAS算法。并发修改时,进行比较,满足条件就更新,否则再次比较,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。

        悲观锁认为不加锁肯定会出问题,使用Java提供的各种锁实现加锁。

        悲观锁适合写操作比较多的情况,乐观锁则适合读多写少的情况,不加锁会带来大量的性能提升。

可重入锁

        可重入锁又名递归锁,当一个线程进入到一个同步方法中,在此方法中要调用另一个同步方法,而且两个方法共用同一把锁,此时线程是可以进入到另一个同步方法中的。

        对于 ReentrantLock 而言,从它的名字就可以看出是一个可重入锁,其名字是 Reentrant Lock 重新进入锁。对于 synchronized 而已,也是一个可重入锁,可重入锁的一个好处是能一定程度避免死锁。

public class Demo {
    synchronized void setA() throws Exception {
        System.out.print("方法A");
        setB();
    }

    synchronized void setB() throws Exception {
        System.out.print("方法B");
    }
}

        上面的代码就是一个可重入锁的一个特点,如果synchronized不是可重入锁,setB方法不会被当前线程执行,从而发生死锁。

读写锁

        读写锁的特点:读读共享,读写互斥,写写互斥。加读写锁是防止另外的线程在此时写入数据,防止读取脏数据。ReentrantReadWriteLock 可以实现写锁和读锁,读写可以用一个锁实现。都是读的时候,多个线程可以共享这把锁(可以同时进入),一旦有写的操作,就要一个一个执行。

public class Demo {
    private int data; // 共享数据
    private ReadWriteLock rwl = new ReentrantReadWriteLock();

    // 写数据
    public void set(int data) {
        rwl.writeLock().lock(); // 取到写锁
        try {
            System.out.println(Thread.currentThread().getName() + "准备写入数据");
            this.data = data;
            System.out.println(Thread.currentThread().getName() + "写入" + this.data);
        } finally {
            rwl.writeLock().unlock(); // 释放写锁
        }
    }

    // 读数据
    public void get() {
        rwl.readLock().lock(); // 取到读锁
        try {
            System.out.println(Thread.currentThread().getName() + "准备读取数据");
            System.out.println(Thread.currentThread().getName() + "读取" + this.data);
        } finally {
            rwl.readLock().unlock(); // 释放读锁
        }
    }
}

分段锁

        JDK8之后,去除了真正的分段锁。现在的分段锁并非一种实际的锁,而是一种实现的思想,将数据分段,并在每个分段上单独加锁,锁的粒度进一步细化。例如 ConcurrentHashMap 没有给方法加锁,将 hash 表中的第一个节点当作锁,这样就可以有多把锁,提高了并发操作的效率。

自旋锁

        不是实际的锁,是获取锁的方式。例如:原子类,改变内存中的变量,需要不断尝试;synchronized,加锁后,其他线程会不断地尝试获取锁。所谓自旋其实指的就是自己重试,当线程枪锁失败后,重试几次,如果抢到锁了就继续,如果抢不到就阻塞线程,说白了还是为了尽量不阻塞线程。自旋锁是比较消耗CPU的,因为要不断循环重试,不会释放CPU资源。另外,加锁时间普遍较短的场景非常适合自旋锁,可以极大提高锁的效率。

共享锁/独占锁

共享锁

        指该锁可被多个线程所持有,并发访问共享资源。比如:读写锁中的读锁,都是读操作时,多个线程可以公用。

独占锁

        也叫互斥锁,指该锁一次只能被一个线程所持有。ReentrantLock,synchronized 都是独占锁。但 Lock 的另一个实现类 ReadWriteLock,其读锁是共享锁,其写锁是独占锁。 

公平锁/非公平锁

公平锁

        按照请求锁的顺序分配,谁先来先获得锁,拥有稳定获得锁的机会。ReentrantLock 默认是非公平锁,但是底层可以通过 AQS 来实现线程调度,所有也可以设置为公平锁

非公平锁

        不按照请求锁的顺序分配,谁先抢到就是谁的,不一定拥有获得锁的机会。synchronized 就是一种非公平锁。

// 默认
public ReentrantLock() {
    sync = new NonfairSync();
}

// 传入true or false,通过两个内部类实现公平锁或非公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

        

        

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值