当前位置: 首页 > article >正文

聊聊java的两种锁同步锁和重入锁

java重入锁和同步锁有什么区别

在Java中,重入锁(ReentrantLock)和同步锁(Synchronized)都是用于实现线程同步的机制,但它们有一些区别。

  1. 可重入性:
    • 重入锁是可重入的,也就是说,同一个线程可以多次获取同一个重入锁而不会产生死锁。在获取锁之后,线程可以多次进入被保护的代码块,并且每次退出代码块时都要释放锁。
    • 同步锁也是可重入的。当一个线程获取到同步锁后,可以再次获取同一个锁而不会产生死锁。同步锁的可重入性是由Java虚拟机自动实现的。
  1. 锁的获取方式:
    • 重入锁需要手动获取和释放,即通过调用lock()方法获取锁,然后在合适的时候调用unlock()方法释放锁。
    • 同步锁是隐式获取和释放的,当线程进入同步代码块时,会自动获取同步锁;当线程退出同步代码块时,会自动释放同步锁。
  1. 粒度:
    • 重入锁提供了更细粒度的控制。可以通过使用tryLock()方法尝试获取锁、使用lockInterruptibly()方法支持可中断的获取锁等。
    • 同步锁相对来说粒度较大,只能使用synchronized关键字来修饰代码块或方法。
  1. 灵活性:
    • 重入锁提供了一些高级功能,如公平性设置、条件变量等,可以更灵活地控制线程的访问顺序和条件等待。
    • 同步锁相对简单,没有提供类似的高级功能。

什么场景用重入锁好

重入锁(ReentrantLock)在以下场景中通常比同步锁(Synchronized)更适用:

  1. 公平性要求:重入锁可以通过构造函数的参数设置为公平锁,从而按照线程请求锁的顺序来获取锁。而同步锁无法直接实现公平性,它总是采用非公平的方式获取锁。
  2. 可中断性要求:重入锁提供了lockInterruptibly()方法,可以在获取锁的过程中响应中断请求。而同步锁在获取锁的过程中无法响应中断请求,只能等待获取锁。
  3. 尝试获取锁:重入锁提供了tryLock()方法,可以尝试获取锁而不会发生阻塞。该方法可以用于实现一些特殊的业务逻辑,例如尝试获取锁一段时间后放弃。
  4. 多个条件变量:重入锁可以使用newCondition()方法创建多个条件变量,可以更灵活地实现线程之间的通信和协作。同步锁只能使用wait()notify()方法进行线程的等待和唤醒。

总的来说,如果需要更高级的功能、更细粒度的控制、公平性要求或可中断性要求,重入锁是一个更好的选择。但在一些简单的同步场景中,同步锁通常更加方便和易用。

重入锁如何锁类

重入锁(ReentrantLock)提供了一个条件变量(Condition)机制,可以用于线程之间的通信和协作。下面是一个示例:

ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();

        new Thread(()->{
// 线程1
            lock.lock();
            try {
                System.out.println("1");
                // 执行一些操作
                while (true) {
                    System.out.println("1 s");
                    condition.await(); // 等待条件变量
                    System.out.println("1 e");
                }
                // 执行其他操作
            } catch (InterruptedException e) {
                System.out.println("1 InterruptedException");
                throw new RuntimeException(e);
    
            } finally {
                lock.unlock();
                System.out.println("1 end");
    
            }
        }).start();

        Thread.sleep(1000);
        new Thread(()->{
// 线程2
            lock.lock();
            try {
                System.out.println("2");
                // 执行一些操作
                condition.signal(); // 唤醒线程1
                System.out.println("2 end");
            } finally {
                lock.unlock();
            }
        }).start();
        Thread.currentThread().join();

在上述示例中,我们创建了一个ReentrantLock对象lock和一个条件变量condition。在线程1中,我们获取锁并执行一些操作,然后在满足某个条件时,调用await()方法等待条件变量。在线程2中,我们获取锁并执行一些操作,然后设置条件变量并调用signal()方法唤醒线程1。

这样就可以使用条件变量来实现线程之间的通信和协作。需要注意的是,在使用条件变量时,需要先获取锁并在try-finally块中释放锁,以确保在任何情况下都能正确释放锁。

此外,需要注意的是,在等待条件变量时,应该总是使用while循环来检查条件是否满足,而不是使用if语句。这是因为,在多线程环境中,可能会出现虚假唤醒的情况,即线程在没有收到信号的情况下被唤醒。使用while循环可以避免这种情况。

同步锁如何锁类

在Java中,可以使用synchronized关键字来锁住类。具体来说,可以在静态方法或静态代码块中使用synchronized关键字来锁住类。下面是一个示例:

public class MyClass {
    // 静态变量
    private static int count = 0;

    // 静态方法
    public static synchronized void increment() {
        count++;
    }

    // 静态代码块
    static {
        synchronized(MyClass.class) {
            // 执行需要同步的代码块
            // ...
        }
    }
}

在上述示例中,我们定义了一个类MyClass,其中包含一个静态变量count和一个静态方法increment()。在increment()方法中,我们使用synchronized关键字来锁住该方法,以确保在同一时刻只有一个线程可以访问该方法。

另外,在静态代码块中,我们使用synchronized关键字来锁住类对象MyClass.class,以确保在同一时刻只有一个线程可以执行静态代码块中的代码。

这样就可以使用synchronized关键字来锁住类,确保在同一时刻只有一个线程可以访问被保护的代码块。需要注意的是,在实际使用中,需要谨慎使用类锁,以避免死锁或性能问题等,当然也可以锁住某个对象。

    public static void main(String[] args) {
        Map<String, String> list = new HashMap<String, String>();

        for (int i = 0; i < 5; i++) {
            String str = new String("hello" + i);
            if (list.containsKey(str)) {
                System.out.println(str+"存在");
            } else {
                String clock = new String("123");
                list.put(str, clock);
            }
        }
        System.out.println(Json.getJsonFromObject(list));
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            String str = new String("hello" + i);
            executorService.execute(() -> {
                String s = list.get(str);
                System.out.println(s + "========" + str);
                synchronized (s) {
                    System.out.println(str + "获得锁");
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(str + "释放锁");
            });
        }
        System.out.println();
    }

在如上的代码示例中,你可以看到使用同步锁来锁住某个固定的对象,以达到细粒度锁的功能。

最后

总体来说,重入锁相对于同步锁提供了更多的灵活性和控制能力,但使用起来也更加复杂。在大部分情况下,使用同步锁已经足够满足线程同步的需求。只有在需要更高级的功能或更细粒度控制时,才需要考虑使用重入锁。

点赞关注评论一键三连,每周分享技术干货、开源项目、实战经验、国外优质文章翻译等,您的关注将是我的更新动力!


http://www.kler.cn/news/160991.html

相关文章:

  • 【Element-ui】Layout与Container组件
  • Python版本与opencv版本的对应关系
  • FFmpeg之将视频转为16:9(横屏)或9:16(竖屏)(三十六)
  • BCI-Two-streams hypothesis(双流假说)
  • 2022年全国大学生数据分析大赛医药电商销售数据分析求解全过程论文及程序
  • Vector Quantized Diffusion Model for Text-to-Image Synthesis
  • 【高数:1 映射与函数】
  • DS1307时钟模块使用记录
  • C:算术移位和逻辑移位傻傻分不清楚
  • 智慧农业技术解决方案:PPT全文32页,附下载
  • 两种做法——判断是否是二叉搜索树
  • 【Proteus仿真】【STM32单片机】简易计算器
  • [ Linux Audio 篇 ] 音频开发入门基础知识
  • 什么是堆内存?参数如何设置?
  • 【LeetCode】2621. 睡眠函数
  • ETLCloud详解,如何实现最佳实践及问题排查
  • 代码随想录算法训练营第五十八天 | 793.每日温度,496.下一个更大元素 I
  • LabVIEW开发自适应降噪ANC
  • vue的propsData
  • 04 ECharts基础入门
  • MySQL和MongoDB简介以及它们之间的区别
  • ThinkPHP6使用Validate验证表单字段唯一
  • 【每日一题】重新规划路线
  • 【C++初阶】六、类和对象(初始化列表、static成员、友元、内部类)
  • 脉冲压缩及MATLAB仿真
  • 数组常用方法
  • 剧本杀小程序搭建:打造线上剧本杀新体验
  • HTML 块级元素与行内元素有哪些以及注意、总结
  • EasyExcel如何读取全部Sheet页数据方法
  • leetcode刷题:611.有效三角形的个数(双指针实现)