多线程
保证数据的一致性有哪些方案呢?(可以通过事务管理、锁机制和版本控制等方式)
- 事务管理:使用数据库事务来确保一组数据库操作要么全部成功提交,要么全部失败回滚。通过
ACID(原子性、一致性、隔离性、持久性)属性,数据库事务可以保证数据的一致性。 - 锁机制:使用锁来实现对共享资源的互斥访问。在 Java 中,可以使用 synchronized 关键字、ReentrantLock 或其他锁机制来控制并发访问,从而避免并发操作导致数据不一致。
- 版本控制:通过乐观锁的方式,在更新数据时记录数据的版本信息,从而避免同时对同一数据进行修改,进而保证数据的一致性。
线程的创建方式有哪些?(1、继承Thread类(简单但限制继承)、2、实现Runnable接口(灵活解耦任务与线程)、3、使用线程池(资源复用高效管理)实现,分别适用于简单场景、灵活开发和高并发需求)

1.继承Thread类
这是最直接的一种方式,用户自定义类继承java.lang.Thread类,重写其run()方法,run()方法中定义了线程执行的具体任务。创建该类的实例后,通过调用start()方法启动线程。
class MyThread extends Thread {
@Override
public void run() {
// 线程执行的代码
}
}
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}采用继承Thread类方式
优点: 编写简单,如果需要访问当前线程,无需使用Thread.currentThread ()方法,直接使用this,即可获得当前线程
缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类
2.实现Runnable接口(底层用了代理模式)
如果一个类已经继承了其他类,就不能再继承Thread类,此时可以实现java.lang.Runnable接口。实现Runnable接口需要重写run()方法,然后将此Runnable对象作为参数传递给Thread类的构造器,创建Thread对象后调用其start()方法启动线程。
class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行的代码
}
}
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}采用实现Runnable接口方式:
优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。
3、使用线程池(Executor框架)
从Java 5开始引入的java.util.concurrent.ExecutorService和相关类提供了线程池的支持,这是一种更高效的线程管理方式,避免了频繁创建和销毁线程的开销。可以通过Executors类的静态方法创建不同类型的线程池。
class Task implements Runnable {
@Override
public void run() {
// 线程执行的代码
}
}
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(10); // 创建固定大小的线程池
for (int i = 0; i < 100; i++) {
executor.submit(new Task()); // 提交任务到线程池执行
}
executor.shutdown(); // 关闭线程池
}采用线程池方式:
缺点:程池增加了程序的复杂度,特别是当涉及线程池参数调整和故障排查时。错误的配置可能导致死锁、资源耗尽等问题,这些问题的诊断和修复可能较为复杂。
优点:线程池可以重用预先创建的线程,避免了线程创建和销毁的开销,显著提高了程序的性能。对于需要快速响应的并发请求,线程池可以迅速提供线程来处理任务,减少等待时间。并且,线程池能够有效控制运行的线程数量,防止因创建过多线程导致的系统资源耗尽(如内存溢出)。通过合理配置线程池大小,可以最大化CPU利用率和系统吞吐量。
调用 interrupt 是如何让线程抛出异常的(停止一个线程的运行)?(线程中断状态初始为false,interrupt()会中断阻塞方法(如sleep())并抛异常,或仅标记状态供线程主动检测以终止任务)( Thread.interrupt() 触发线程中断状态,结合中断检测逻辑实现安全停止)
每个线程都一个与之关联的布尔属性来表示其中断状态,中断状态的初始值为false,当一个线程被其它线程调用Thread.interrupt()方法中断时,会根据实际情况做出响应。
- 如果该线程正在执行低级别的可中断方法(如Thread.sleep()、Thread.join()或Object.wait()),则会解除阻塞并抛出InterruptedException异常。
- 否则Thread.interrupt()仅设置线程的中断状态,在该被中断的线程中
稍后可通过轮询中断状态来决定是否要停止当前正在执行的任务。
Java线程的状态有哪些?

源自《Java并发编程艺术》 java.lang.Thread.State枚举类中定义了六种线程的状态,可以调用线程Thread中的getState()方法获取当前线程的状态。
线程状态 | 解释 |
NEW | 尚未启动的线程状态(线程已创建,但未调用 |
RUNNABLE | 就绪或运行中状态(调用 |
BLOCKED | 线程因等待监视器锁(如 |
WAITING | 无限期等待其他线程操作(如 |
TIMED_WAITING | 带超时时间的等待(如 |
TERMINATED | 线程执行完毕,终止状态 |
sleep 和 wait的区别是什么?(sleep() 是 不释放锁的线程暂停(自动恢复),而 wait() 是 释放锁的线程协作(需主动唤醒或超时))
对比例表:
特性 |
|
|
所属类 |
|
|
锁释放 | ❌ 不释放锁 | ✅ 释放当前持有的锁 |
使用前提 | 任意位置直接调用 | 必须在同步代码块内(需持有锁) |
唤醒机制 | 超时后自动恢复 | 依赖 |
设计用途 | 暂停线程,不涉及锁协作 | 线程间协调,释放锁让其他线程工作 |
sleep会释放cpu吗?(会,线程会释放 CPU 时间片让其他线程运行,但不会释放已持有的锁)
BLOCKED和WAITING 有啥区别(BLOCKED 是 锁竞争失败后被动进入 的状态(锁释放后自动恢复),而 WAITING 是 主动调用等待方法触发 的状态(需显式唤醒或超时))

notify 和 notifyAll 的区别?( notify() 随机唤醒一个等待线程(其他线程仍阻塞),而 notifyAll() 唤醒所有等待线程使其竞争锁(最终仍只有一个获得锁),前者可能导致线程"饥饿",后者更公平但可能引发资源争抢。)
不同的线程之间如何通信?
共享变量是最基本的线程间通信方式。多个线程可以访问和修改同一个共享变量,从而实现信息的传递。为了保证线程安全,通常需要使用 synchronized 关键字或 volatile 关键字。
volatile (无法保证操作的原子性,适用于简单状态标记)
class SharedVariableExample {
// 使用 volatile 关键字保证变量的可见性
private static volatile boolean flag = false;
public static void main(String[] args) {
// 生产者线程
Thread producer = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 修改共享变量
flag = true;
System.out.println("Producer: Flag is set to true.");
});
// 消费者线程
Thread consumer = new Thread(() -> {
while (!flag) {
// 等待共享变量被修改
}
System.out.println("Consumer: Flag is now true.");
});
producer.start();
consumer.start();
}
}代码解释
volatile 关键字确保了 flag 变量在多个线程之间的可见性,即一个线程修改了 flag 的值,其他线程能立即看到。
生产者线程在睡眠 2 秒后将 flag 设置为 true,消费者线程在 flag 为 false 时一直等待,直到 flag 变为 true 才继续执行。
synchronized + wait/notify(Object 类中的)
Object 类中的 wait()、notify() 和 notifyAll() 方法可以用于线程间的协作。wait() 方法使当前线程进入等待状态,notify() 方法唤醒在此对象监视器上等待的单个线程,notifyAll() 方法唤醒在此对象监视器上等待的所有线程。
class WaitNotifyExample {
private static final Object lock = new Object();
public static void main(String[] args) {
// 生产者线程
Thread producer = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Producer: Producing...");
Thread.sleep(2000);
System.out.println("Producer: Production finished. Notifying consumer.");
// 唤醒等待的线程
lock.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Consumer: Waiting for production to finish.");
// 进入等待状态
lock.wait();
System.out.println("Consumer: Production finished. Consuming...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
consumer.start();
producer.start();
}
}代码解释:
lock 是一个用于同步的对象,生产者和消费者线程都需要获取该对象的锁才能执行相应的操作。
消费者线程调用 lock.wait() 方法进入等待状态,释放锁;生产者线程执行完生产任务后调用 lock.notify() 方法唤醒等待的消费者线程。
ReentrantLock(显式锁)和 Condition(条件变量)
java.util.concurrent.locks 包中的 Lock 和 Condition 接口提供了比 synchronized 更灵活的线程间通信方式。Condition 接口的 await() 方法类似于 wait() 方法,signal() 方法类似于 notify() 方法,signalAll() 方法类似于 notifyAll() 方法。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class LockConditionExample {
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
public static void main(String[] args) {
// 生产者线程
Thread producer = new Thread(() -> {
lock.lock();
try {
System.out.println("Producer: Producing...");
Thread.sleep(2000);
System.out.println("Producer: Production finished. Notifying consumer.");
// 唤醒等待的线程
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
lock.lock();
try {
System.out.println("Consumer: Waiting for production to finish.");
// 进入等待状态
condition.await();
System.out.println("Consumer: Production finished. Consuming...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
consumer.start();
producer.start();
}
}代码解释:
ReentrantLock 是 Lock 接口的一个实现类,condition 是通过 lock.newCondition() 方法创建的。
消费者线程调用 condition.await() 方法进入等待状态,生产者线程执行完生产任务后调用 condition.signal() 方法唤醒等待的消费者线程。
















