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 安装依然卡,需要好久才完成
1178 0
|
机器学习/深度学习 JSON 前端开发
RESTful API接口设计规范
近年来移动互联网的发展,前端设备层出不穷(手机、平板、桌面电脑、其他专用设备…),因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信,于是RESTful诞生了,它可以通过一套统一的接口为 Web,iOS和Android提供服务。
4045 1
RESTful API接口设计规范
Glide Caused by: javax.net.ssl.SSLPeerUnverifiedException: Hostname not verified:
Glide Caused by: javax.net.ssl.SSLPeerUnverifiedException: Hostname not verified:
1514 0
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
298 59
|
安全 Java 应用服务中间件
渗透测试-JBoss 5.x/6.x反序列化漏洞
渗透测试-JBoss 5.x/6.x反序列化漏洞
227 14
|
Go 开发工具 git
在Qemu+ARM上运行Minix3内核
在Qemu+ARM上运行Minix3内核
|
运维 Ubuntu 网络协议
Ubuntu系统下修改网卡IP地址
【7月更文挑战第3天】Ubuntu系统下修改网卡IP地址
2086 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万连接数。可扩展公网带宽和云盘。适用于入门级企业应用。
753 0
|
弹性计算 运维 Kubernetes
OAM 深入解读(一):OAM 为云原生应用带来哪些价值?
# 背景 OAM 是阿里巴巴联合微软在社区推出的一款用于构建和交付云原生应用的标准规范,之前我们已经发布过一系列介绍文章,为方便大家查阅,链接和介绍如下: * [《Open Application Model (OAM) 实践指南》](https://www.atatech.org/articles/155780):具体而详实的介绍了OAM方方面面的细节。 * [《给 K8s API
|
监控 网络协议 算法
TCP 拥塞控制详解 | 6. 主动队列管理
TCP 拥塞控制详解 | 6. 主动队列管理
754 1
TCP 拥塞控制详解 | 6. 主动队列管理