Java多线程(每天两道面试题)

多线程有使用过吗?

用过,使用多线程,要保证多线程是安全的,不要出现数据竞争,造成数据混乱的问题

Java的线程安全体现在三个方面:

  • 原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作

    • 在Java中,使用了 atomic类synchronized 这个关键字,来确保原子性
  • 可见性:一个线程对主内存的修改可以及时地被其他线程看到

    • 在Java中,使用了synchronizedvolatile 这两个关键字,确保可见性
  • 有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序

    • 在Java中使用了 happens-before 原则,来确保有序性

在Synchronized的代码中怎么提高速度?

synchronized 是保证线程安全的重要机制,但它会带来性能开销

以下是提高 synchronized 代码块性能的几种方法:

  • 减少同步范围:尽量缩小 synchronized 代码块的范围,只对必要的代码进行同步,减少线程阻塞的时间
Java 复制代码
// 不推荐 - 同步整个方法  
public synchronized void process() {  
    // 大量不需要同步的代码  
    // ...  
    // 少量需要同步的代码  
}  
  
// 推荐 - 只同步关键部分  
public void process() {  
    // 不需要同步的代码  
    // ...  
    synchronized(this) {  
        // 需要同步的少量代码  
    }  
}
  • 使用更细粒度的锁:若有多个独立的资源需要同步,可使用多个不同的锁,而非一个全局锁,以此降低锁的竞争
Java 复制代码
// 不推荐 - 所有操作共用同一把锁  
privatefinal Object lock = new Object();  
  
public void updateA() {  
    synchronized(lock) { /* 操作A */ }  
}  
  
public void updateB() {  
    synchronized(lock) { /* 操作B */ }  
}  
  
// 推荐 - 不同操作使用不同的锁  
privatefinal Object lockA = new Object();  
privatefinal Object lockB = new Object();  
  
public void updateA() {  
    synchronized(lockA) { /* 操作A */ }  
}  
  
public void updateB() {  
    synchronized(lockB) { /* 操作B */ }  
}
  • 使用读写锁:若对共享资源的读操作远多于写操作,可使用 ReadWriteLock 来提高并发性能
    • 读锁可以被多个线程同时持有,而写锁是排他的
Java 复制代码
import java.util.concurrent.locks.ReadWriteLock;  
import java.util.concurrent.locks.ReentrantReadWriteLock;  
  
privatefinal ReadWriteLock rwLock = new ReentrantReadWriteLock();  
  
public void readOperation() {  
    rwLock.readLock().lock();  
    try {  
        // 读操作  
    } finally {  
        rwLock.readLock().unlock();  
    }  
}  
  
public void writeOperation() {  
    rwLock.writeLock().lock();  
    try {  
        // 写操作  
    } finally {  
        rwLock.writeLock().unlock();  
    }  
}
  • 使用原子类:对于一些简单的操作,如计数、递增等,可以使用 Java 的原子类(如 AtomicInteger),使用 CAS(Compare-And-Swap)操作,避免了使用 synchronized 带来的锁竞争
Java 复制代码
import java.util.concurrent.atomic.AtomicInteger;  
  
public class AtomicExample {  
    private AtomicInteger count = new AtomicInteger(0);  
  
    public void increment() {  
        count.incrementAndGet();  
    }  
  
    public int getCount() {  
        return count.get();  
    }  
}

总结:

  1. 减少同步范围
  2. 使用更细粒度的锁
  3. 使用读写锁
  4. 使用原子类或CAS操作
相关推荐
秋难降4 分钟前
SQL 索引突然 “罢工”?快来看看为什么
数据库·后端·sql
围巾哥萧尘40 分钟前
Anthropic Claude for Chrome🧣
面试
Access开发易登软件1 小时前
Access开发导出PDF的N种姿势,你get了吗?
后端·低代码·pdf·excel·vba·access·access开发
中国胖子风清扬2 小时前
Rust 序列化技术全解析:从基础到实战
开发语言·c++·spring boot·vscode·后端·中间件·rust
bobz9652 小时前
分析 docker.service 和 docker.socket 这两个服务各自的作用
后端
要记得喝水2 小时前
C#某公司面试题(含题目和解析)--1
开发语言·windows·面试·c#·.net
野犬寒鸦2 小时前
力扣hot100:旋转图像(48)(详细图解以及核心思路剖析)
java·数据结构·后端·算法·leetcode
岁忧3 小时前
(LeetCode 面试经典 150 题) 200. 岛屿数量(深度优先搜索dfs || 广度优先搜索bfs)
java·c++·leetcode·面试·go·深度优先
phiilo3 小时前
golang 设置进程退出时kill所有子进程
后端
花花无缺3 小时前
python自动化-pytest-用例发现规则和要求
后端·python