java多线程——显式锁 vs 隐式锁

简介: java多线程——显式锁 vs 隐式锁

所谓显式锁和隐式锁,主要指的就是 synchronized 关键字和  ReentrantLock 类。


下面具体聊一聊二者之间的区别:



1 底层不同


synchronized 是java中的关键字,是JVM层面的锁。


ReentrantLock 是是JDK5以后出现的具体的类。使用lock是调用对应的API。


我们通过Javap命令来查看调用二者的汇编指令:


可以看出 synchronized 是底层是通过monitorenter进行加锁,通过monitorexit来退出锁的。


ReentrantLock 对象是通过调用对应的API方法来获取锁和释放锁的。

68.png


2  使用方式不同


正如文章的标题那样,显式锁和隐式锁最大的不同就是在使用的时候,程序员要不要手动写代码去获取锁和释放锁的操作。


在使用 synchronized 关键字的时候,我们使用者根本不用写其他的代码,然后程序就能够获取锁和释放锁了。那是因为当synchronized 代码块执行完成之后,系统会自动的让程序释放占用的锁。 如下:


public class Demo9 {
    public static void main(String[] args) {
        //线程不安全
        //解决方案2  同步方法
        Runnable runnable = new Ticket();
        new Thread(runnable).start();
        new Thread(runnable).start();
        new Thread(runnable).start();
    }
    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        @Override
        public void run() {
            while (true) {
                boolean flag = sale();
                if(!flag){
                    break;
                }
            }
        }
        public synchronized boolean sale(){
            if (count > 0) {
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                return true;
            }
                return false;
        }
    }
}


在使用lock的时候,我们使用者需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。


手动获取锁方法:


lock.lock();


手动释放锁:


lock.unlock();


举例如下:


public class Demo10 {
    public static void main(String[] args) {
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        private Lock l = new ReentrantLock();
        @Override
        public void run() {
            while (true) {
                l.lock();
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                    }else {
                        break;
                    }
                l.unlock();
            }
        }
    }
}


3 是否可以中断


synchronized 是不可中断的。除非抛出异常或者正常运行完成


lock可以中断的。中断方式:


调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断


4 是否是公平锁


synchronized 是非公平锁


lock创建时可以传入一个Boolean值来设置是公平锁还是非公平锁:


true:公平锁

false:非公平锁


5 性能


在java1.6之前synchronized性能较低,因为其需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还要多,相比之下lock的性能更高一些。


然而到了java1.6之后对锁进行了很多的优化,进而出现轻量级锁,偏向锁,锁消除,适应性自旋锁导致synchronized的性能也不差,并且由于其在语义上很清晰所以也被官方更多的支持。


6 使用锁的方式


lock 获取锁与释放锁的过程,都需要程序员手动的控制 Lock用的是乐观锁方式。


乐观锁:每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。


synchronized托管给jvm执行 原始采用的是CPU悲观锁机制


悲观锁:线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。


相关文章
|
2天前
|
安全 Java 编译器
线程安全问题和锁
本文详细介绍了线程的状态及其转换,包括新建、就绪、等待、超时等待、阻塞和终止状态,并通过示例说明了各状态的特点。接着,文章深入探讨了线程安全问题,分析了多线程环境下变量修改引发的数据异常,并通过使用 `synchronized` 关键字和 `volatile` 解决内存可见性问题。最后,文章讲解了锁的概念,包括同步代码块、同步方法以及 `Lock` 接口,并讨论了死锁现象及其产生的原因与解决方案。
23 10
线程安全问题和锁
|
2天前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
1天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
3天前
|
缓存 监控 Java
java中线程池的使用
java中线程池的使用
|
6天前
|
Oracle Java 关系型数据库
【颠覆性升级】JDK 22:超级构造器与区域锁,重塑Java编程的两大基石!
【9月更文挑战第6天】JDK 22的发布标志着Java编程语言在性能和灵活性方面迈出了重要的一步。超级构造器和区域锁这两大基石的引入,不仅简化了代码设计,提高了开发效率,还优化了垃圾收集器的性能,降低了应用延迟。这些改进不仅展示了Oracle在Java生态系统中的持续改进和创新精神,也为广大Java开发者提供了更多的可能性和便利。我们有理由相信,在未来的Java编程中,这些新特性将发挥越来越重要的作用,推动Java技术不断向前发展。
|
2天前
|
算法 Java 数据处理
Java并发编程:解锁多线程的力量
在Java的世界里,掌握并发编程是提升应用性能和响应能力的关键。本文将深入浅出地探讨如何利用Java的多线程特性来优化程序执行效率,从基础的线程创建到高级的并发工具类使用,带领读者一步步解锁Java并发编程的奥秘。你将学习到如何避免常见的并发陷阱,并实际应用这些知识来解决现实世界的问题。让我们一起开启高效编码的旅程吧!
|
7天前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
4天前
|
Java 开发者
Java中的多线程编程基础与实战
【9月更文挑战第6天】本文将通过深入浅出的方式,带领读者了解并掌握Java中的多线程编程。我们将从基础概念出发,逐步深入到代码实践,最后探讨多线程在实际应用中的优势和注意事项。无论你是初学者还是有一定经验的开发者,这篇文章都能让你对Java多线程有更全面的认识。
14 1
|
2天前
|
安全 Java UED
Java并发编程:解锁多线程的潜力
在Java的世界里,并发编程如同一场精心编排的交响乐,每个线程扮演着不同的乐手,共同奏响性能与效率的和声。本文将引导你走进Java并发编程的大门,探索如何在多核处理器上优雅地舞动多线程,从而提升应用的性能和响应性。我们将从基础概念出发,逐步深入到高级技巧,让你的代码在并行处理的海洋中乘风破浪。
|
16天前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
43 1