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操作
相关推荐
前端小巷子14 分钟前
Vue CLI相关配置
前端·vue.js·面试
风象南1 小时前
Spring Boot + SSH 客户端:在浏览器中执行远程命令
后端
程序员爱钓鱼1 小时前
Go语言实战案例:Cookie与Session基础
前端·后端·go
程序员爱钓鱼1 小时前
Go语言实战案例:用户注册与登录(无数据库)
后端·google·go
在未来等你1 小时前
RabbitMQ面试精讲 Day 13:HAProxy与负载均衡配置
中间件·面试·消息队列·rabbitmq
Code季风3 小时前
Redis 缓存:应对缓存雪崩、缓存击穿和缓存穿透
数据库·redis·分布式·后端·缓存·微服务·mybatis
hrrrrb10 小时前
【Spring Boot 快速入门】八、登录认证(一)基础登录与认证校验
spring boot·后端
王大锤·11 小时前
基于spring boot的个人博客系统
java·spring boot·后端
bobz96511 小时前
QT designer 常用技巧
后端
shi578311 小时前
C# 常用的线程同步方式
开发语言·后端·c#