并行和并发有什么区别?

  • 并行指的是多个处理器同时执行不同的任务,即真正的并行执行。

  • 并发指的是在单个处理器上看起来同时执行多个任务,实际上是通过切换任务来模拟同时执行的效果。

线程,进程和协程的区别?

  • 进程是系统资源分配的基本单位,每个进程拥有独立的地址空间。

  • 线程是进程内的执行单元,同一进程内的线程共享进程的资源,包括内存空间。

  • 协程是用户空间的控制结构,不需要操作系统参与调度。协程之间的切换通常比线程切换更快。

守护线程是什么?

守护线程是在后台运行的服务线程,它的主要作用是为用户线程提供服务。当所有非守护线程结束时,不管守护线程是否还在运行,程序都会退出。

创建线程有哪几种方式?

  • 实现Runnable接口。

  • 继承Thread类。

  • 使用CallableFuture(配合ExecutorService)。

ThreadPoolExecutor 构造函数
这个构造函数有多个重载版本,但最常用的一个包含以下参数:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • 参数:

    • corePoolSize: 线程池的基本大小。

    • maximumPoolSize: 线程池允许的最大线程数量。

    • keepAliveTime: 当线程池中的线程数量大于 corePoolSize 时,多余的空闲线程的存活时间。

    • unit: keepAliveTime 参数的时间单位。

    • workQueue: 用于存放等待执行的任务的阻塞队列。

    • threadFactory: 用于创建新线程的工厂。

    • handler: 当拒绝新任务时的处理程序。

说一下 runnable 和 callable 有什么区别?

  • Runnable接口定义了一个run()方法,不返回结果。

  • Callable接口定义了一个call()方法,可以返回结果并通过Future获取。

线程有哪些状态?

  • NEW: 新建状态。

  • RUNNABLE: 就绪/运行状态。

  • BLOCKED: 阻塞状态(等待锁)。

  • WAITING: 等待状态(无限期等待)。

  • TIMED_WAITING: 等待状态(有限期等待)。

  • TERMINATED: 死亡状态。

sleep()和wait() 有什么区别?

  • sleep()Thread类的静态方法,让当前线程暂停指定的时间。

  • wait()Object类的方法,使线程进入等待状态,通常需要与synchronized块一起使用。

notify()和 notifyAll()有什么区别?

  • notify()唤醒正在等待此对象监视器的一个线程。

  • notifyAll()唤醒所有等待此对象监视器的线程。

线程的run()和start()有什么区别?

  • start()方法启动一个新线程,并调用run()方法。

  • run()方法只是简单地执行线程的任务逻辑。

创建线程池有哪几种方式?

  • 使用Executors工具类提供的工厂方法。

  • 使用ThreadPoolExecutor手动创建。

线程池都有哪些状态?

  • RUNNING: 接受新任务,处理队列中的任务。

  • SHUTDOWN: 不接受新任务,处理队列中的任务。

  • STOP: 不接受新任务,不处理队列中的任务,中断正在执行的任务。

  • TIDYING: 所有任务都终止,工作线程退出。

  • TERMINATED: terminated()方法被调用。

线程池中submit()和execute()方法有什么区别?

  • execute()提交一个Runnable任务,没有返回值。

  • submit()提交一个Runnable或Callable任务,返回一个Future,可以用来获取异步计算的结果。

在java程序中怎么保证多线程的运行安全?

  • 使用synchronized关键字。

  • 使用java.util.concurrent包中的并发工具类,如AtomicIntegerReentrantLockSemaphore等。

  • 使用线程局部变量ThreadLocal

多线程锁的升级原理是什么?

  • 锁升级是指随着竞争情况的不同,锁可以从偏向锁升级到轻量级锁,再升级到重量级锁的过程。

  • 升级的目的是为了减少锁带来的性能消耗。

什么是死锁?

死锁是指两个或多个线程相互等待对方持有的锁,从而无法继续执行的情况。

怎么防止死锁?

  • 遵循良好的锁定协议,如按顺序锁定资源。

  • 使用超时机制,避免无限期等待。

  • 使用工具检测潜在的死锁条件。

ThreadLocal是什么?有哪些使用场景?

  • ThreadLocal提供了线程本地变量,每个线程都有自己的副本,互不影响。

  • 使用场景包括事务管理、日志记录、HTTP请求等需要在线程间隔离数据的情况。

说一下synchronized底层实现原理?

  • synchronized最初基于锁记录和监视器实现,使用Monitor对象控制访问。

  • 后续版本引入了偏向锁和轻量级锁优化。

synchronized和volatile的区别是什么?

  • synchronized提供原子性和可见性,可以用来同步代码块。

  • volatile仅提供可见性和禁止指令重排,不能保证原子性。

synchronized和Lock有什么区别?

  • synchronized是内置关键字,自动释放锁。

  • Lock是接口,需要手动获取和释放锁。

synchronized和ReentrantLock区别是什么?

  • synchronized是内置关键字,自动释放锁。

  • ReentrantLockLock接口的实现,提供了更灵活的锁机制。

生产者消费者

在计算机科学中,“生产者-消费者”模型是一种经典的多线程编程模式,用于处理并发数据生产和消费的问题。这个模型描述了两种类型的进程或线程之间的关系:生产者负责生成数据,而消费者则负责处理这些数据。这种模式通常用于实现队列或缓冲区,以协调不同线程或进程之间的数据流。

生产者-消费者模型的关键组件

  1. 生产者:负责创建数据项,并将其放入共享数据结构(如队列或缓冲区)中。

  2. 消费者:从共享数据结构中取出数据项并处理它们。

  3. 共享数据结构:通常是一个队列或者缓冲区,用来存放生产者产生的数据,供消费者取出使用。

关键问题

  • 同步:确保生产者不会在队列已满的情况下继续向队列中添加数据,同样确保消费者不会在队列为空的情况下尝试从中取出数据。

  • 互斥:防止多个线程同时修改共享数据结构。

  • 死锁:避免程序由于错误的同步而导致无法继续执行的情况发生。

解决方案

为了实现有效的生产者-消费者模型,需要考虑以下解决方案:

  1. 互斥锁:确保每次只有一个线程能够访问共享数据结构。

  2. 条件变量:用于同步生产者和消费者。当队列为空时,消费者等待;当队列满时,生产者等待。

  3. 信号量:可以用来控制多个生产者和消费者之间的访问权限,例如限制队列中的最大项目数量。