面试小札:闪电五连鞭_6

devtools/2024/12/23 2:48:32/

1. synchronized和ReentrantLock的区别
 
- 实现机制
-  synchronized 是Java中的关键字,它是基于JVM内部实现的。JVM会自动对 synchronized 修饰的方法或代码块进行加锁和解锁操作。例如:
public class SynchronizedExample {
    public synchronized void method() {
        // 代码块
    }
}
 
当一个线程访问 method 方法时,JVM会自动在方法的入口处获取对象锁,在方法执行结束后自动释放锁。
-  ReentrantLock 是一个Java类,它属于 java.util.concurrent.locks 包。它通过显式地调用 lock 方法来获取锁,调用 unlock 方法来释放锁。例如:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
    private ReentrantLock lock = new ReentrantLock();
    public void method() {
        lock.lock();
        try {
            // 代码块
        } finally {
            lock.unlock();
        }
    }
}
 
 
这种显式的加锁和解锁操作需要开发者自己保证 unlock 方法一定会被调用,通常会放在 finally 块中,以防止出现异常时锁无法释放。
- 功能特性
-  ReentrantLock 提供了更灵活的功能。它可以实现公平锁和非公平锁,通过构造函数 ReentrantLock(boolean fair) 来指定。例如, new ReentrantLock(true) 创建一个公平锁。公平锁是指按照线程请求锁的顺序来分配锁,而非公平锁则不保证这一点,允许插队。 synchronized 是一种非公平锁。
-  ReentrantLock 提供了 tryLock 方法。 tryLock 方法可以尝试获取锁,如果获取成功则返回 true ,否则返回 false ,而不会像 synchronized 那样一直阻塞等待。它还有 tryLock(long timeout, TimeUnit unit) 方法,可以在指定的时间内尝试获取锁。
- 性能方面
- 在低竞争情况下(即很少有线程同时竞争锁), synchronized 的性能和 ReentrantLock 差不多。但在高竞争情况下, ReentrantLock 的性能可能会更好,因为它可以通过更灵活的策略来处理锁的竞争。不过, synchronized 在后续的Java版本中不断优化,性能差距在逐渐缩小。


2. Atomic的原理
 
- 原子性操作
-  Atomic 类(如 AtomicInteger 、 AtomicLong 等)是Java提供的用于实现原子性操作的类。原子性操作是指不可被中断的操作,在多线程环境下,一个原子操作一旦开始,就会一直执行到结束,不会被其他线程干扰。以 AtomicInteger 为例,它提供了一些原子性的方法,如 getAndIncrement 、 incrementAndGet 等。
- 基于CAS(Compare - And - Swap)机制
- 其核心原理是CAS。CAS操作包含三个操作数:内存位置(V)、旧的预期值(A)和新值(B)。在更新变量时,首先会比较内存位置的值与旧的预期值,如果相等,则将内存位置的值更新为新值;如果不相等,则说明有其他线程已经修改了该值,操作就会失败。例如, AtomicInteger 的 getAndIncrement 方法的实现大致如下:
public final int getAndIncrement() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return current;
    }
}
 
 
这里, compareAndSet 方法就是实现CAS操作的方法。它会不断地尝试,直到成功地将值更新为 next 。这种方式避免了使用锁带来的开销,在高并发场景下,当竞争不是特别激烈时,可以提供高效的原子操作。


3. 乐观锁和悲观锁的理解、实现及实现方式
 
- 理解乐观锁和悲观锁
- 乐观锁:乐观地认为在大部分情况下,数据在并发访问时不会产生冲突。它不会对数据进行加锁,而是在更新数据时,通过一种机制来检查数据是否被其他线程修改过。如果没有被修改,则进行更新;如果被修改了,则根据具体的策略(如重试或者抛出异常)来处理。
- 悲观锁:悲观地认为在数据的并发访问过程中,很容易产生冲突。所以在访问数据时,就会先对数据进行加锁,以防止其他线程对数据进行访问。只有持有锁的线程才能对数据进行操作,当操作完成后才会释放锁。
- 实现方式
- 乐观锁实现
- 版本号机制:在数据库表中添加一个版本号(version)字段。例如,在更新数据时,先查询数据的版本号,在更新操作中带上版本号的条件判断。假设要更新一条用户记录,SQL语句可能如下:
UPDATE user_table SET name = 'new_name', version = version + 1 WHERE id = 1 AND version = 1;
 
 
这里,只有当 id = 1 的记录的版本号是 1 时,才会更新成功,并且更新后版本号会加 1 。
- CAS机制(如前面Atomic类中提到的):在Java中, Atomic 类就是基于CAS实现的乐观锁。在不使用锁的情况下,通过不断地比较和交换来实现对数据的原子性操作。
- 悲观锁实现
- 在Java中使用 synchronized 关键字:当 synchronized 修饰方法或者代码块时,就是一种悲观锁的实现。例如:
public class PessimisticLockExample {
    private int data;
    public synchronized void updateData() {
        // 对data进行修改操作
    }
}
 
 
当一个线程访问 updateData 方法时,会获取对象锁,其他线程必须等待该线程释放锁后才能访问。
- 在数据库中使用排它锁(FOR UPDATE):在SQL中,例如在关系型数据库(如MySQL)中,当执行 SELECT...FOR UPDATE 语句时,会对查询到的数据行加排它锁。例如:
SELECT * FROM user_table WHERE id = 1 FOR UPDATE;
 
 
这条SQL语句会对 id = 1 的记录加排它锁,其他事务对同一记录的读取和修改操作都会被阻塞,直到当前事务释放锁。


4. synchronized是哪种锁的实现
 
-  synchronized 是一种可重入的互斥锁(排他锁)实现。
- 可重入性:例如,一个线程在进入一个被 synchronized 修饰的方法后,如果这个方法内部又调用了另一个被 synchronized 修饰的方法(属于同一个对象),该线程可以直接进入而不需要重新获取锁。这是因为 Java 在实现 synchronized 时,会记录锁的持有线程和持有次数。
- 互斥性:当一个线程获取了对象的 synchronized 锁后,其他线程想要访问同一个对象的被 synchronized 修饰的方法或者代码块时,必须等待该线程释放锁。例如,在一个多线程的 Java 类中:
public class SynchronizedMutexExample {
    public synchronized void method1() {
        // 代码块
    }
    public synchronized void method2() {
        // 代码块
    }
}
 
 
如果线程A正在执行 method1 ,线程B想要执行 method1 或者 method2 ,就必须等待线程A释放对象锁。


5. new ReentrantLock()创建的是公平锁还是非公平锁
 
-  new ReentrantLock() 创建的是一个非公平锁。
- 非公平锁的特点是当锁可用时,新到达的线程可以立即获取锁,而不必等待等待队列中的线程。这样在高并发情况下可能会导致某些线程长时间等待,但是非公平锁的性能通常比公平锁要好,因为它减少了线程切换和等待的开销。如果需要创建公平锁,可以使用 new ReentrantLock(true) 。


http://www.ppmy.cn/devtools/144559.html

相关文章

【单片机原理】第1章 微机基础知识,运算器,控制器,寄存器,微机工作过程,数制转换

关注作者了解更多 我的其他CSDN专栏 过程控制系统 工程测试技术 虚拟仪器技术 可编程控制器 工业现场总线 数字图像处理 智能控制 传感器技术 嵌入式系统 复变函数与积分变换 单片机原理 线性代数 大学物理 热工与工程流体力学 数字信号处理 光电融合集成电路…

基于 Node.js 的开源轻量简洁 API 调试工具:Hoppscotch

对于使用过 Postman 或 ApiPost 的开发者来说,可能找过比其更加简洁、轻量的API调试工具。本篇文章就为大家推荐一款开源、免费、轻量、简洁、美观的API调试工具:Hoppscotch。 项目介绍 Hoppscotch 是一款基于 Node.js 开发的免费、开源、且便捷美观的 …

Pytorch | 从零构建ResNet对CIFAR10进行分类

Pytorch | 从零构建ResNet对CIFAR10进行分类 CIFAR10数据集ResNet核心思想网络结构创新点优点应用 ResNet结构代码详解结构代码代码详解BasicBlock 类ResNet 类ResNet18、ResNet34、ResNet50、ResNet101、ResNet152函数 训练过程和测试结果代码汇总resnet.pytrain.pytest.py 前…

基于Spring Boot的校园车辆管理系统

一、系统背景与意义 随着校园规模的不断扩大和车辆数量的增加,传统的车辆管理方式已经难以满足高效、准确管理车辆的需求。因此,开发一个基于Spring Boot的校园车辆管理系统具有重要的现实意义。该系统可以实现对校园车辆的信息化管理,提高车…

使用Redis提升PHP应用的性能

使用Redis提升PHP应用的性能 在现代Web应用开发中,性能优化是确保用户体验的关键因素之一。Redis,作为一种高性能的内存数据存储系统,因其快速的读写速度和丰富的数据结构而广泛应用于各种场景。本文将探讨如何在PHP应用中使用Redis来提升性…

如何通过docker 部署minio,端口号为9105

通过Docker部署MinIO对象存储服务,并指定API端口为9105,可以按照以下步骤进行。我们将基于已有的资料来详细说明这一过程。 1. 准备工作 首先,确保你的系统上已经安装了Docker。如果没有安装,可以根据官方文档指导完成安装。接下…

ARM学习(38)多进程多线程之间的通信方式

ARM学习(38)ARM学习(38)多进程多线程之间的通信方式 一、问题背景 笔者在调试模拟器的时候,碰到进程间通信的问题,一个进程在等另外一个进程ready的时候,迟迟等不到,然后通过调试发现,另外一个进程变量已经变化了,但是当前进程变量没变化,需要了解进程间通信的方式…

【蓝桥杯】43699-四平方和

四平方和 题目描述 四平方和定理,又称为拉格朗日定理: 每个正整数都可以表示为至多 4 个正整数的平方和。如果把 0 包括进去,就正好可以表示为 4 个数的平方和。 比如: 502021222 712121222; 对于一个给定的正整数,可…