首页 > 编程语言 >Java 中的 volatile和synchronized和 ReentrantLock区别讲解和案例示范

Java 中的 volatile和synchronized和 ReentrantLock区别讲解和案例示范

时间:2024-10-01 10:49:23浏览次数:10  
标签:Java synchronized lock void ReentrantLock 线程 public

在 Java 的并发编程中,volatilesynchronizedReentrantLock 是三种常用的同步机制。每种机制都有其独特的特性、优缺点和适用场景。理解它们之间的区别以及在何种情况下使用哪种机制,对提高程序的性能和可靠性至关重要。本文将详细探讨这三种机制的特性、使用场景及示例。

1. volatile 的特性

1.1 保证可见性

volatile 修饰的变量确保所有线程都能看到变量的最新值,避免了线程间的缓存不一致问题。

示例
public class VolatileVisibility {
    private volatile boolean running = true;

    public void stop() {
        running = false; // 修改 running
    }

    public void execute() {
        while (running) {
            // 执行任务
        }
    }
}

在这个例子中,running 变量被声明为 volatile,当一个线程调用 stop() 方法时,其他线程会立即看到 running 的值为 false

1.2 禁止指令重排序

volatile 还保证了对该变量的操作不会被重排序,从而确保程序执行的顺序性。

示例
public class VolatileReordering {
    private int a = 0;
    private volatile int b = 0;

    public void method1() {
        a = 1; // 1
        b = 2; // 2
    }

    public void method2() {
        if (b == 2) { // 3
            System.out.println(a); // 4
        }
    }
}

如果没有 volatile,可能会发生重排序,使得 method2()method1()b = 2 之前执行,导致 a 的值可能为 0。

1.3 不保证原子性

volatile 不能保证对复合操作的原子性,比如自增操作。

示例
public class VolatileAtomicity {
    private volatile int count = 0;

    public void increment() {
        count++; // 非原子操作
    }

    public int getCount() {
        return count;
    }
}

在这个例子中,increment() 方法对 count 的自增不是原子操作,可能导致数据不一致。

2. synchronized 的特性

2.1 可重入性

synchronized 允许同一线程多次获得同一把锁,而不会发生死锁。

示例
public class SynchronizedReentrancy {
    public synchronized void method1() {
        method2(); // 允许重入
    }

    public synchronized void method2() {
        // 执行任务
    }
}

在这个例子中,method1() 可以安全地调用 method2(),因为 synchronized 允许可重入。

2.2 不可中断性

synchronized 的获取锁是不可中断的,线程在等待锁时不能被中断。

示例
public class SynchronizedInterruptibility {
    public synchronized void lockedMethod() throws InterruptedException {
        Thread.sleep(10000); // 模拟长时间执行
    }

    public void execute() {
        Thread thread = new Thread(() -> {
            try {
                lockedMethod();
            } catch (InterruptedException e) {
                // 处理被中断
            }
        });
        thread.start();
        thread.interrupt(); // 线程在等待锁时被中断
    }
}

在这个例子中,lockedMethod() 无法被中断,导致线程无法释放锁。

2.3 锁的升级和降级

synchronized 支持锁的升级和降级。在方法中直接使用 synchronized,在代码块中也可以使用。

示例
public class SynchronizedUpgrade {
    public synchronized void method() {
        // 持有对象锁
        synchronized (this) {
            // 持有同一把锁
        }
    }
}

在这个例子中,使用了对象的锁和类的锁,展示了锁的升级和降级。

2.4 不公平性

synchronized 不保证公平性,可能导致某些线程长时间等待。

示例
public class SynchronizedFairness {
    public synchronized void method() {
        // 执行任务
    }
}

在这个例子中,多个线程访问 method() 时,无法保证先请求的线程先获得锁。

2.5 可见性、原子性和有序性

synchronized 保证了对共享变量的可见性、原子性和有序性。

示例
public class SynchronizedVisibility {
    private int data;

    public synchronized void updateData(int value) {
        data = value; // 更新数据
    }

    public synchronized int readData() {
        return data; // 读取数据
    }
}

在这个例子中,updateData()readData() 方法保证了 data 的线程安全。

3. ReentrantLock 的特性

3.1 可重入性

ReentrantLock 允许同一线程多次获得锁,支持可重入。

示例
public class ReentrantLockReentrancy {
    private final ReentrantLock lock = new ReentrantLock();

    public void method() {
        lock.lock();
        try {
            method(); // 允许重入
        } finally {
            lock.unlock();
        }
    }
}

在这个例子中,method() 方法可以安全地调用自身,因为 ReentrantLock 允许可重入。

3.2 可中断性

ReentrantLock 允许在等待锁时被中断,提供了更好的控制。

示例
public class ReentrantLockInterruptibility {
    private final ReentrantLock lock = new ReentrantLock();

    public void lockedMethod() throws InterruptedException {
        lock.lockInterruptibly(); // 可中断的锁
        try {
            // 执行任务
        } finally {
            lock.unlock();
        }
    }
}

在这个例子中,lockedMethod() 可以被中断,提供了更好的控制。

3.3 公平性和非公平性

ReentrantLock 支持公平和非公平锁的选择。

示例
ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
ReentrantLock unfairLock = new ReentrantLock(); // 非公平锁

在这个例子中,公平锁会按照线程请求的顺序来获取锁,而非公平锁则可能导致某些线程饥饿。

3.4 条件变量

ReentrantLock 提供了条件变量支持,可以实现复杂的线程间协作。

示例
public class ReentrantLockCondition {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void await() throws InterruptedException {
        lock.lock();
        try {
            condition.await(); // 等待条件
        } finally {
            lock.unlock();
        }
    }

    public void signal() {
        lock.lock();
        try {
            condition.signal(); // 唤醒等待的线程
        } finally {
            lock.unlock();
        }
    }
}

在这个例子中,使用条件变量实现了线程间的协作。

4. 三者之间的区别

特性volatilesynchronizedReentrantLock
可见性保证可见性保证可见性保证可见性
互斥性不保证保证互斥性保证互斥性
是否可重入不适用支持可重入支持可重入
代码块范围只能用于变量代码块或方法代码块或方法
锁的获取方式自动获取显式获取
公平性支持公平性
性能性能开销小性能开销中等性能开销较大

5. 适用场景分析

5.1 何时使用 volatile

适用场景:当需要保证某个变量的可见性,但不需要互斥访问时,使用 volatile 是最佳选择。

示例
public class VolatileFlag {
    private volatile boolean flag = true;

    public void stop() {
        flag = false;
    }

    public void run() {
        while (flag) {
            // 执行任务
        }
    }
}

在这个场景中,volatile 可以有效地减少上下文切换,提高性能。

5.2 何时使用 synchronized

适用场景:当需要对共享资源进行互斥访问时,使用 synchronized 是最佳选择。

示例
public class SynchronizedCounter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

在这个例子中,synchronized 确保了 count 的线程安全。

5.3 何时使用 ReentrantLock

适用场景:当需要更加灵活的锁定机制,比如可重入性、公平性或可中断的锁时,使用 ReentrantLock 是最佳选择。

示例
public class ReentrantLockCounter {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}

在这个例子中,ReentrantLock 允许灵活的控制锁的获取和释放。

6. 总结

通过本文对 volatilesynchronizedReentrantLock 的深入分析,读者可以了解到它们各自的特性、优缺点及适用场景。在并发编程中,选择合适的同步机制不仅可以提高程序性能,还能有效地避免潜在的线程安全问题。

在实际开发中,根据不同的需求和场景,合理使用这三种机制,可以使得 Java 程序在并发执行时更加高效和安全。

标签:Java,synchronized,lock,void,ReentrantLock,线程,public
From: https://blog.csdn.net/weixin_39996520/article/details/142644589

相关文章

  • JavaScript笔记
    基操数据类型原始类型对象类型MapandSet流程控制函数及面向对象函数方法常用内部对象面向对象编程(OOP)操作BOM元素操作DOM元素(I)操作表单jQuery基操js作为一种脚本语言,可以嵌入到HTML页面中js是双标签,可以写多行,也可以写一行内部标签<script>......
  • 基于java+ssm的大中型企业职工信息化平台设计
    前言......
  • java计算机毕业设计“小世界”私人空间(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在信息爆炸的时代,个人隐私与个性化空间的需求日益增长。随着社交媒体和数字化生活的普及,人们渴望在繁忙的虚拟世界中拥有一片属于自己的“小世界”。......
  • java计算机毕业设计购物商城(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网的飞速发展,电子商务已成为现代商业的重要组成部分。购物商城作为电子商务的主要形式之一,凭借其便捷性、高效性和丰富的商品选择,吸引了大量......
  • java计算机毕业设计汉中市旅游综合服务平台(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景汉中市,位于中国陕西省西南部,是一座历史悠久、文化底蕴深厚的城市,拥有丰富的自然和人文景观。随着旅游业的快速发展,汉中市的旅游资源日益受到国内外游......
  • java计算机毕业设计扶贫助农与产品合作系统(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在当今社会,扶贫助农已成为国家发展的重要议题。随着科技的进步和互联网的普及,传统的扶贫方式正逐步向数字化、智能化转型。然而,农村地区由于信息不对......
  • 教你如何免费获取股票数据用python、JavaScript (Node.js)、JAVA等多种语言的实例代码
    ​近一两年来,股票量化分析逐渐受到广泛关注。而作为这一领域的初学者,首先需要面对的挑战就是如何获取全面且准确的股票数据。因为无论是实时交易数据、历史交易记录、财务数据还是基本面信息,这些数据都是我们进行量化分析时不可或缺的宝贵资源。我们的核心任务是从这些数据......
  • 【多线程奇妙屋】 Java 的 Thread类必会小技巧,教你如何用多种方式快速创建线程,学并发
    本篇会加入个人的所谓鱼式疯言❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言而是理解过并总结出来通俗易懂的大白话,小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.......
  • java计算机毕业设计保护大自然网站的设计与实现(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着工业化与城市化进程的加速,人类对自然资源的索取日益加剧,环境污染与生态破坏问题愈发严峻。森林砍伐、水源污染、生物多样性丧失等环境问题,不仅威......
  • java计算机毕业设计大学生创新创业项目管理系统(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着高等教育的普及和深化,大学生创新创业已成为推动社会经济发展的重要力量。然而,传统的项目管理方式往往存在流程繁琐、效率低下、信息不透明等问题......