synchronized 锁与 ReentrantLock 锁的区别

简介: synchronized 锁与 ReentrantLock 锁的区别

相关知识点参考:ConcurrentHashMap 可以使用 ReentrantLock 作为锁吗?

线程可以通过加 synchronized 锁或 ReentrantLock 锁两种方式对代码加锁,实现线程安全。他们的区别如下:

共同点:

(1)都保证了可见性和互斥性

(2)都是可重入锁,同一线程可以多次获得同一个锁

(3)都是用来协调多线程对共享对象、变量的访问

不同点:

(1)synchronized 是 Java 关键字,ReentrantLock 是 lock 的实现类。

(2)synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码

(3)synchronized 在程序发生异常或执行完时会自动释放锁,ReentrantLock 必须要手动释放锁(用 unlock 方法),如果不释放就成了死锁,因此使用 lock 时需要在 finally 块中释放锁。

(4)底层实现不一样,synchronized 是同步阻塞,使用悲观并发策略;ReentrantLock 是同步非阻塞,采用乐观并发策略,所以可以提高多个线程进行读操作的效率。

(5)synchronized 可以把任意一个非 NULL 的对象当做锁,它属于独占式的悲观锁、非公平锁,同时也属于可重入锁。ReentrantLock 继承接口 Lock 并实现了接口中定义的方法,它也是一种可重入锁,同时可以将其设置为公平锁。

(6)synchronized 无法判断获取锁的状态,ReentrantLock 可以判断线程是否获取到了锁,并且可以对获取锁的等待时间进行设置,避免死锁,同时可以获取各种锁的信息以及可以灵活地实现多路通知。

 

ReentrantLock 公平性的设置:

ReentrantLock fairLock = new ReentrantLock(true),参数为 true 时,表明设置为公平锁,会倾向于将锁赋予等待时间最久的线程。

公平锁:获取锁的顺序按先后调用 lock()方法的顺序(慎用),也就是排队一样。

非公平锁:抢占的顺序不一定,随机的。

注意:一般没有强需求按顺序执行的情况下,采用非公平锁好,因为 Java 的 JVM 设置很少情况下才会出现一个线程等待很久没有被分配到资源的情况。然后采用了公平锁的话,会造成一定的性能消耗,导致吞吐量下降。

synchronized 加锁实现方式:

关于同步方法的总结:

  1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
  2. 非静态的同步方法,其同步监视器是:this(当前类的对象)

静态同步方法:同步监视器是:当前类本身(类.class ( ))

class Ticket{
    private int num=100;//总共100张票
    public synchronized void sell() throws InterruptedException {
        //此时用synchronized修饰方法sell(),其同步监视器是:this(当前类的对象),由于使用的是
        while(num>0){
            System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+(num--));
            //为了创造一些异常,让线程到此处sleep阻塞一下
            TimeUnit.MILLISECONDS.sleep(10);
        }
    }
}
class Ticket{
    private int num=100;//总共100张票
    public void sell() throws InterruptedException {
        synchronized (this){
            //此时使用synchronized修饰代码块
            while (num > 0) {
                System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + (num--));
                //为了创造一些异常,让线程到此处sleep阻塞一下1
                TimeUnit.MILLISECONDS.sleep(10);
            }
        }
    }
}

ReentrantLock 加锁实现方式:

public void buy() {
        lock.lock();
        try {
            while (num==0){
                //此时没有商品,需要等待
                notNull.await();
            }
            //不为0则可以进行操作
            num--;
            System.out.println(Thread.currentThread().getName()+"消费商品,当前剩余商品:"+num);
           notNull.signalAll();//通知其他线程可以生产商品
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
相关文章
npm 切换镜像后,npm i 安装依然卡,需要好久才完成
npm 切换镜像后,npm i 安装依然卡,需要好久才完成
1085 0
|
存储 SQL 分布式计算
Lakehouse架构指南
Lakehouse架构指南
365 2
|
前端开发 JavaScript API
React 之 Suspense
React 之 Suspense
396 0
Glide Caused by: javax.net.ssl.SSLPeerUnverifiedException: Hostname not verified:
Glide Caused by: javax.net.ssl.SSLPeerUnverifiedException: Hostname not verified:
1300 0
|
10月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
223 59
|
Go 开发工具 git
在Qemu+ARM上运行Minix3内核
在Qemu+ARM上运行Minix3内核
|
运维 Ubuntu 网络协议
Ubuntu系统下修改网卡IP地址
【7月更文挑战第3天】Ubuntu系统下修改网卡IP地址
1361 1
|
弹性计算 固态存储 JavaScript
阿里云4核8G云服务器ECS有哪些?性能如何?4C8G性能参数表
阿里云4核8G ECS u1实例,适合30并发,日均1万IP访问。当前优惠价700元/年。配置包括Intel Xeon CPU,2.5 GHz,4核8G内存,1-3M带宽,20-40GB ESSD系统盘。网络性能可达50万PPS,最高25万连接数。可扩展公网带宽和云盘。适用于入门级企业应用。
592 0
|
JavaScript
深入理解 Vue.js 中的`mapState`辅助函数:简化状态管理的秘密武器(下)
深入理解 Vue.js 中的`mapState`辅助函数:简化状态管理的秘密武器(下)
深入理解 Vue.js 中的`mapState`辅助函数:简化状态管理的秘密武器(下)
|
传感器 机器学习/深度学习 安全
智能家电
智能家电
360 1