Java线程详解

embedded/2025/1/15 7:11:52/

一、线程的基本概念

1. 什么是线程

  • 线程是程序执行的一个单元,它是进程中的一个实体,是被系统独立调度和分派的基本单位。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间和文件句柄,但每个线程有自己的程序计数器、栈和局部变量等。

2. 线程的优势

  • 提高资源利用率:在多处理器或多核系统中,多个线程可以同时运行在不同的处理器核心上,充分利用系统资源,提高程序的执行效率。
  • 提高响应性:对于用户界面程序,将耗时的操作放在后台线程执行,主线程继续响应用户操作,提高用户体验。

二、线程的创建和启动

1. 继承 Thread

java">class MyThread extends Thread {@Overridepublic void run() {// 线程执行的代码System.out.println("Thread is running");}
}public class ThreadExample {public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动线程}
}
  • 解释:
    • 定义一个类 MyThread 继承 Thread 类,并重写 run() 方法,将线程要执行的代码放在 run() 方法中。
    • main() 方法中创建 MyThread 实例,并调用 start() 方法启动线程,调用 start() 会使线程进入就绪状态,等待系统调度执行,而不是直接调用 run() 方法,直接调用 run() 方法只是普通的方法调用,不会启动新线程

2. 实现 Runnable 接口

java">class MyRunnable implements Runnable {@Overridepublic void run() {// 线程执行的代码System.out.println("Thread is running");}
}public class RunnableExample {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start();}
}
  • 解释:
    • 定义一个类 MyRunnable 实现 Runnable 接口,并重写 run() 方法。
    • 创建 MyRunnable 的实例,将其作为参数传递给 Thread 类的构造函数,然后调用 start() 方法启动线程

3. 使用 CallableFuture(带返回值的线程

java">import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {// 线程执行的代码return 42;}
}public class CallableExample {public static void main(String[] args) throws ExecutionException, InterruptedException {MyCallable myCallable = new MyCallable();FutureTask<Integer> futureTask = new FutureTask<>(myCallable);Thread thread = new Thread(futureTask);thread.start();// 获取线程的返回值Integer result = futureTask.get();System.out.println("Thread result: " + result);}
}
  • 解释:
    • 定义一个类 MyCallable 实现 Callable 接口,重写 call() 方法,该方法可以有返回值,并且可以抛出异常。
    • 创建 FutureTask 对象,将 MyCallable 实例作为参数传递给 FutureTask 的构造函数。
    • 创建 Thread 对象,将 FutureTask 作为参数传递给 Thread 的构造函数并启动线程
    • 使用 futureTask.get() 方法获取线程执行的结果,该方法会阻塞直到线程执行完成并返回结果,如果线程未完成,调用线程会等待。

三、线程的生命周期

Java 线程的生命周期主要包括以下几个状态:

  • 新建(New):当创建了 Thread 类的实例,但还未调用 start() 方法时,线程处于新建状态。
  • 就绪(Runnable):调用 start() 方法后,线程进入就绪状态,等待系统调度。
  • 运行(Running):当线程被系统选中并开始执行 run()call() 方法时,线程处于运行状态。
  • 阻塞(Blocked)线程可能因为等待锁、等待 I/O 操作完成、调用 wait() 方法等而进入阻塞状态,暂时停止执行。
  • 等待(Waiting)线程调用 wait()join()LockSupport.park() 等方法会进入等待状态,直到其他线程通知或中断。
  • 超时等待(Timed Waiting)线程调用 sleep()wait(long)join(long)LockSupport.parkNanos() 等方法,在等待一段时间后自动唤醒。
  • 终止(Terminated)线程执行完 run()call() 方法,或者出现异常而终止。

四、线程的同步

1. synchronized 关键字

  • 同步方法:
java">class Counter {private int count = 0;public synchronized void increment() {count++;}public int getCount() {return count;}
}
  • 解释:

    • synchronized 修饰方法时,同一时间只有一个线程可以执行该方法,保证了方法的同步。
  • 同步代码块:

java">class Counter {private int count = 0;private final Object lock = new Object();public void increment() {synchronized (lock) {count++;}}public int getCount() {return count;}
}
  • 解释:
    • 使用 synchronized 同步代码块,通过一个对象锁(这里是 lock 对象)来保证代码块内的代码在同一时间只有一个线程可以执行。

2. ReentrantLock

java">import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class Counter {private int count = 0;private final Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}
  • 解释:
    • 使用 ReentrantLock 类,在进入代码块前调用 lock() 方法加锁,在代码块执行完后在 finally 块中调用 unlock() 方法释放锁,确保锁的释放,避免死锁。

五、线程间通信

1. wait()notify()notifyAll() 方法

java">class MessageQueue {private final Object lock = new Object();private String message;public void put(String message) {synchronized (lock) {while (this.message!= null) {try {lock.wait();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}this.message = message;lock.notifyAll();}}public String take() {synchronized (lock) {while (message == null) {try {lock.wait();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}String result = message;message = null;lock.notifyAll();return result;}}
}
  • 解释:
    • wait() 方法使线程进入等待状态,直到被 notify()notifyAll() 方法唤醒。
    • notify() 唤醒一个等待的线程notifyAll() 唤醒所有等待的线程

2. BlockingQueue 接口及其实现类

java">import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;public class BlockingQueueExample {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);queue.put("Message 1");String message = queue.take();System.out.println(message);}
}
  • 解释:
    • BlockingQueue 是一个阻塞队列,提供了 put()take() 等方法,当队列满时 put() 会阻塞,当队列空时 take() 会阻塞,简化了线程间的数据传递。

六、线程

1. 使用 ExecutorServiceThreadPoolExecutor

java">import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {executorService.execute(() -> {System.out.println("Thread is running");});}executorService.shutdown();}
}
  • 解释:
    • Executors.newFixedThreadPool(5) 创建一个固定大小为 5 的线程池。
    • executorService.execute() 方法将任务提交给线程池执行。
    • executorService.shutdown() 方法关闭线程池。

2. 自定义 ThreadPoolExecutor

java">import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;public class CustomThreadPoolExample {public static void main(String[] args) {int corePoolSize = 2;int maximumPoolSize = 5;long keepAliveTime = 10;TimeUnit unit = TimeUnit.SECONDS;ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);ThreadFactory threadFactory = Executors.defaultThreadFactory();RejectedExecutionHandler handler = new AbortPolicy();ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);for (int i = 0; i < 10; i++) {executor.execute(() -> {System.out.println("Thread is running");});}executor.shutdown();}
}
  • 解释:
    • ThreadPoolExecutor 提供了更灵活的线程池配置,包括核心线程数、最大线程数、线程存活时间、队列容量等。

七、线程的调度和优先级

1. 线程优先级

java">class MyThread extends Thread {public MyThread(String name) {super(name);}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " is running");}
}public class ThreadPriorityExample {public static void main(String[] args) {MyThread thread1 = new MyThread("Thread 1");MyThread thread2 = new MyThread("Thread 2");thread1.setPriority(Thread.MAX_PRIORITY);thread2.setPriority(Thread.MIN_PRIORITY);thread1.start();thread2.start();}
}
  • 解释:
    • setPriority() 方法可以设置线程的优先级,范围从 1(最低)到 10(最高),但优先级只是一个建议,实际的调度由操作系统决定。

2. 线程调度

  • Java 线程的调度由操作系统负责,操作系统根据线程的状态和优先级等因素来调度线程的执行,开发人员可以通过 yield() 方法让当前线程让出 CPU 资源,让其他线程有机会执行,但不保证一定有效。

八、线程的中断

1. interrupt() 方法

java">class MyThread extends Thread {@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {// 线程执行的代码}System.out.println("Thread interrupted");}
}public class ThreadInterruptExample {public static void main(String[] args) throws InterruptedException {MyThread thread = new MyThread();thread.start();Thread.sleep(1000);thread.interrupt();}
}
  • 解释:
    • interrupt() 方法用于中断线程,调用该方法会设置线程的中断标志。
    • 线程可以通过 isInterrupted() 方法检查中断标志,或者在阻塞操作中抛出 InterruptedException 来响应中断。

九、并发工具类

1. CountDownLatch

java">import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(3);new Thread(() -> {latch.countDown();}).start();new Thread(() -> {latch.countDown();}).start();new Thread(() -> {latch.countDown();}).start();latch.await();System.out.println("All threads have finished");}
}
  • 解释:
    • CountDownLatch 可以让一个或多个线程等待其他线程完成操作,countDown() 方法将计数器减 1,await() 方法使当前线程等待计数器为 0。

2. CyclicBarrier

java">import java.util.concurrent.CyclicBarrier;public class CyclicBarrierExample {public static void main(String[] args) {CyclicBarrier barrier = new CyclicBarrier(3, () -> {System.out.println("All threads have reached the barrier");});new Thread(() -> {try {barrier.await();} catch (Exception e) {e.printStackTrace();}}).start();new Thread(() -> {try {barrier.await();} catch (Exception e) {e.printStackTrace();}}).start();new Thread(() -> {try {barrier.await();} catch (Exception e) {e.printStackTrace();}}).start();}
}
  • 解释:
    • CyclicBarrier 让一组线程互相等待,当所有线程都到达屏障时,执行指定的操作。

3. Semaphore

java">import java.util.concurrent.Semaphore;public class SemaphoreExample {public static void main(String[] args) {Semaphore semaphore = new Semaphore(2);new Thread(() -> {try {semaphore.acquire();System.out.println("Thread 1 acquired the semaphore");semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}).start();new Thread(() -> {try {semaphore.acquire();System.out.println("Thread 2 acquired the semaphore");semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}
  • 解释:
    • Semaphore 是一个计数信号量,控制同时访问某个资源的线程数量,acquire() 方法获取许可证,release() 方法释放许可证。

通过上述的讲解,你可以对 Java 线程的创建、生命周期、同步、通信、池化、调度、中断以及并发工具类有一个较为全面的了解。在实际开发中,根据不同的场景选择合适的线程和并发工具,可以提高程序的性能和可维护性。同时,需要注意多线程编程中的并发问题,如死锁、资源竞争等,确保程序的正确性和稳定性。

Java 线程在实际应用中的典型例子

一、文件下载器

假设你正在开发一个文件下载器应用程序,为了提高下载速度,可以使用多线程同时下载文件的不同部分。

java">import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class FileDownloader {private static final int THREAD_COUNT = 5;private static final int BUFFER_SIZE = 4096;public static void main(String[] args) throws Exception {String fileUrl = "http://example.com/largefile.zip";String savePath = "largefile.zip";URL url = new URL(fileUrl);URLConnection connection = url.openConnection();int fileSize = connection.getContentLength();int partSize = fileSize / THREAD_COUNT;ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);try (OutputStream outputStream = new FileOutputStream(savePath)) {for (int i = 0; i < THREAD_COUNT; i++) {int startByte = i * partSize;int endByte = (i == THREAD_COUNT - 1)? fileSize - 1 : (i + 1) * partSize - 1;executorService.execute(() -> {try {downloadPart(url, outputStream, startByte, endByte);} catch (Exception e) {e.printStackTrace();}});}} finally {executorService.shutdown();}}private static void downloadPart(URL url, OutputStream outputStream, int startByte, int endByte) throws Exception {URLConnection connection = url.openConnection();connection.setRequestProperty("Range", "bytes=" + startByte + "-" + endByte);try (InputStream inputStream = connection.getInputStream()) {byte[] buffer = new byte[BUFFER_SIZE];int bytesRead;long totalBytesRead = 0;while ((bytesRead = inputStream.read(buffer))!= -1 && totalBytesRead < (endByte - startByte + 1)) {outputStream.write(buffer, 0, bytesRead);totalBytesRead += bytesRead;}}}
}

解释

  • 该程序使用 ExecutorService 创建一个固定大小的线程池,将文件分成多个部分,每个线程负责下载文件的一部分。
  • downloadPart 方法通过设置 Range 请求头来指定下载文件的范围,确保每个线程下载文件的不同部分。
  • 每个线程从输入流中读取数据并写入输出流,最终将文件的不同部分拼接成完整的文件。

二、服务器并发处理

在开发一个简单的服务器程序时,可以使用多线程处理客户端的并发请求。

java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class SimpleServer {private static final int PORT = 8080;private static final int THREAD_POOL_SIZE = 10;public static void main(String[] args) throws IOException {ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);try (ServerSocket serverSocket = new ServerSocket(PORT)) {System.out.println("Server is listening on port " + PORT);while (true) {Socket socket = serverSocket.accept();executorService.execute(() -> handleClient(socket));}} finally {executorService.shutdown();}}private static void handleClient(Socket socket) {try (BuffufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter writer = new PrintWriter(socket.getOutputStream(), true)) {String clientRequest = reader.readLine();System.out.println("Received from client: " + clientRequest);String response = "Hello, client!";writer.println(response);} catch (IOException e) {e.printStackTrace();} finally {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}
}

解释

  • ServerSocket 监听指定端口,当有客户端连接时,将连接交给线程池中的线程处理。
  • handleClient 方法处理客户端请求,读取客户端发送的数据并发送响应,确保每个客户端请求都能得到及时处理。

三、生产者-消费者模式

在生产者-消费者模式中,生产者生成数据,消费者消费数据,中间通过一个队列存储数据。

java">import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;class Producer implements Runnable {private final BlockingQueue<String> queue;public Producer(BlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {String data = "Data " + i;queue.put(data);System.out.println("Produced: " + data);Thread.sleep(100);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}class Consumer implements Runnable {private final BlockingQueue<String> queue;public Consumer(BlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {try {String data = queue.take();System.out.println("Consumed: " + data);Thread.sleep(200);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}public class ProducerConsumerExample {public static void main(String[] args) {BlockingQueue<String> queue = new LinkedBlockingQueue<>();Thread producerThread = new Thread(new Producer(queue));Thread consumerThread = new Thread(new Consumer(queue));producerThread.start();consumerThread.start();}
}

解释

  • Producer 类负责生产数据并将其放入 BlockingQueue 中,Consumer 类从队列中取出数据并消费。
  • BlockingQueue 保证了线程安全,当队列满时生产者阻塞,当队列空时消费者阻塞,避免了生产者和消费者之间的同步问题。

四、并行计算

对于一些计算密集型任务,可以使用多线程进行并行计算,例如计算斐波那契数列。

java">import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;class FibonacciTask implements Callable<Integer> {private final int n;public FibonacciTask(int n) {this.n = n;}@Overridepublic Integer call() throws Exception {if (n <= 1) {return n;}return fibonacci(n - 1) + fibonacci(n - 2);}private int fibonacci(int n) {if (n <= 1) {return n;}return fibonacci(n - 1) + fibonacci(n - 2);}
}public class ParallelFibonacci {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(4);Future<Integer> future1 = executorService.submit(new FibonacciTask(30));Future<Integer> future2 = executorService.submit(new FibonacciTask(35));System.out.println("Fibonacci(30): " + future1.get());System.out.println("Fibonacci(35): " + future2.get());executorService.shutdown();}
}

解释

  • FibonacciTask 类实现 Callable 接口,计算斐波那契数列的第 n 项。
  • ExecutorService 用于提交多个 FibonacciTask线程池进行并行计算,通过 Future 类的 get() 方法获取计算结果。

五、定时任务

使用 Java 线程实现定时任务,可以使用 ScheduledExecutorService

java">import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ScheduledTask {public static void main(String[] args) {ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);executorService.scheduleAtFixedRate(() -> {System.out.println("Task executed at " + System.currentTimeMillis());}, 0, 1, TimeUnit.SECONDS);}
}

解释

  • ScheduledExecutorService 用于执行定时任务,scheduleAtFixedRate 方法按照固定的时间间隔执行任务。

通过这些实际应用示例,可以看到 Java 线程在不同场景下的使用方式,包括并发处理、资源下载、生产者-消费者模式、并行计算和定时任务。在实际开发中,合理使用线程可以显著提高程序的性能和响应速度,但需要注意线程安全和资源竞争等问题。根据具体的业务需求和性能要求,可以灵活运用不同的线程创建和管理方式。

在上述示例中,不同的场景使用了不同的线程创建和管理方式,包括 ExecutorServiceThreadPoolExecutorRunnableCallableFuture 等,你可以根据自己的需求进行调整和扩展。

以下是 Java 线程在面试中经常会被问到的一些问题:

一、基础概念类问题

1. 什么是线程线程和进程的区别是什么?
  • 回答
    • 线程是程序执行的一个单元,是进程中的一个实体,是被系统独立调度和分派的基本单位。一个进程可以包含多个线程,它们共享进程的资源,如内存空间、文件句柄等,但每个线程拥有自己的程序计数器、栈和局部变量。
    • 进程是程序的一次执行过程,是系统资源分配的基本单位,包括代码、数据和系统资源,进程之间相互独立,拥有独立的内存空间,进程间的切换开销较大。而线程是进程中的执行单元,线程切换的开销相对较小,因为它们共享进程的资源。
2. 如何创建一个线程?有哪些方式?
  • 回答
    • 继承 Thread
java">class MyThread extends Thread {@Overridepublic void run() {// 线程要执行的代码System.out.println("Thread is running");}
}public class ThreadExample {public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动线程}
}
- **实现 `Runnable` 接口**:
java">class MyRunnable implements Runnable {@Overridepublic void run() {// 线程要执行的代码System.out.println("Thread is running");}
}public class RunnableExample {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start();}
}
- **使用 `Callable` 和 `Future`(带返回值的线程)**:
java">import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {// 线程要执行的代码return 42;}
}public class CallableExample {public static void main(String[] args) throws ExecutionException, InterruptedException {MyCallable myCallable = new MyCallable();FutureTask<Integer> futureTask = new FutureTask<>(myCallable);Thread thread = new Thread(futureTask);thread.start();// 获取线程的返回值Integer result = futureTask.get();System.out.println("Thread result: " + result);}
}

二、线程状态类问题

1. 请描述 Java 线程的生命周期。
  • 回答
    • 新建(New):创建 Thread 类的实例,但尚未调用 start() 方法时,线程处于新建状态。
    • 就绪(Runnable):调用 start() 方法后,线程进入就绪状态,等待系统分配 CPU 资源。
    • 运行(Running)线程获得 CPU 资源,开始执行 run()call() 方法。
    • 阻塞(Blocked)线程因等待锁、I/O 操作、调用 wait() 等进入阻塞状态,暂时停止执行。
    • 等待(Waiting)线程调用 wait()join()LockSupport.park() 等进入等待状态,等待其他线程唤醒。
    • 超时等待(Timed Waiting)线程调用 sleep()wait(long)join(long)LockSupport.parkNanos() 等进入等待一段时间后自动唤醒的状态。
    • 终止(Terminated)线程完成任务或出现异常而终止。
2. 如何让一个线程暂停和恢复执行?
  • 回答
    • 暂停:可以使用 Thread.sleep(long)线程暂停一段时间;使用 wait() 方法使线程进入等待状态,需要其他线程调用 notify()notifyAll() 唤醒;使用 join() 让当前线程等待另一个线程执行完毕。
    • 恢复:使用 notify()notifyAll() 唤醒等待的线程;对于 join(),当被等待的线程执行完毕,当前线程会自动恢复。

三、线程同步类问题

1. 解释 synchronized 关键字的作用。
  • 回答
    • synchronized 可以用于修饰方法或代码块,保证同一时间只有一个线程可以访问被修饰的方法或代码块,实现线程同步,防止多个线程同时访问共享资源导致的数据不一致问题。例如:
java">class Counter {private int count = 0;public synchronized void increment() {count++;}public int getCount() {return count;}
}

java">class Counter {private int count = 0;private final Object lock = new Object();public void increment() {synchronized (lock) {count++;}}public int getCount() {return count;}
}
2. 什么是死锁?如何避免死锁?
  • 回答
    • 死锁:多个线程互相等待对方释放锁,导致程序无法继续执行的情况。例如,线程 A 持有锁 X 并等待锁 Y,线程 B 持有锁 Y 并等待锁 X,就会发生死锁。
    • 避免死锁的方法
      • 按顺序获取锁,避免循环等待。
      • 尽量减少锁的使用,缩小同步代码块的范围。
      • 使用 tryLock() 方法尝试获取锁,避免死锁。
3. 如何使用 ReentrantLock 进行线程同步?
  • 回答
java">import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class Counter {private int count = 0;private final Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}
  • ReentrantLock 提供了更灵活的锁机制,通过 lock() 加锁,在 finally 块中使用 unlock() 释放锁,避免死锁。

四、线程间通信类问题

1. 解释 wait()notify()notifyAll() 的使用方法。
  • 回答
    • wait() 使线程进入等待状态,释放锁,直到其他线程调用 notify()notifyAll() 唤醒。
    • notify() 唤醒一个等待的线程notifyAll() 唤醒所有等待的线程。例如:
java">class MessageQueue {private final Object lock = new Object();private String message;public void put(String message) {synchronized (lock) {while (this.message!= null) {try {lock.wait();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}this.message = message;lock.notifyAll();}}public String take() {synchronized (lock) {while (message == null) {try {lock.wait();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}String result = message;message = null;lock.notifyAll();return result;}}
}
2. 如何使用 BlockingQueue 实现线程间通信?
  • 回答
    • BlockingQueue 是一个阻塞队列,提供 put()take() 方法,当队列满时 put() 阻塞,当队列空时 take() 阻塞,方便线程间的数据传递。例如:
java">import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;public class BlockingQueueExample {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);queue.put("Message 1");String message = queue.take();System.out.println(message);}
}

五、线程池类问题

1. 什么是线程池?为什么要使用线程池?
  • 回答
2. 如何创建和使用线程池?
  • 回答
    • 可以使用 ExecutorServiceThreadPoolExecutor 创建线程池,例如:
java">import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {executorService.execute(() -> {System.out.println("Thread is running");});}executorService.shutdown();}
}

java">import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;public class CustomThreadPoolExample {public static void main(String[] args) {int corePoolSize = 2;int maximumPoolSize = 5;long keepAliveTime = 10;TimeUnit unit = TimeUnit.SECONDS;ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);ThreadFactory threadFactory = Executors.defaultThreadFactory();RejectedExecutionHandler handler = new AbortPolicy();ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);for (int i = 0; i < 10; i++) {executor.execute(() -> {System.out.println("Thread is running");});}executor.shutdown();}
}

六、并发工具类问题

1. 解释 CountDownLatch 的作用和使用方法。
  • 回答
    • CountDownLatch 可以让一个或多个线程等待其他线程完成操作。例如:
java">import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(3);new Thread(() -> {latch.countDown();}).start();new Thread(() -> {latch.countDown();}).start();new Thread(() -> {latch.countDown();}).start();latch.await();System.out.println("All threads have finished");}
}
  • 多个线程调用 countDown() 减少计数,当计数为 0 时,调用 await()线程继续执行。
2. 解释 CyclicBarrier 的作用和使用方法。
  • 回答
    • CyclicBarrier 让一组线程互相等待,当所有线程都到达屏障时,执行指定操作。例如:
java">import java.util.concurrent.CyclicBarrier;public class CyclicBarrierExample {public static void main(String[] args) {CyclicBarrier barrier = new CyclicBarrier(3, () -> {System.out.println("All threads have reached the barrier");});new Thread(() -> {try {barrier.await();} catch (Exception e) {e.printStackTrace();}}).start();new Thread(() -> {try {barrier.await();} catch (Exception e) {e.printStackTrace();}}).start();new Thread(() -> {try {barrier.await();} catch (Exception e) {e.printStackTrace();}}).start();}
}
3. 解释 Semaphore 的作用和使用方法。
  • 回答
    • Semaphore 是一个计数信号量,控制同时访问某个资源的线程数量。例如:
java">import java.util.concurrent.Semaphore;public class SemaphoreExample {public static void main(String[] args) {Semaphore semaphore = new Semaphore(2);new Thread(() -> {try {semaphore.acquire();System.out.println("Thread 1 acquired the semaphore");semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}).start();new Thread(() -> {try {semaphore.acquire();System.out.println("Thread 2 acquired the semaphore");semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}

七、线程安全类问题

1. 如何保证线程安全?
  • 回答
    • 使用 synchronized 关键字或 ReentrantLock 进行同步。
    • 使用线程安全的集合类,如 ConcurrentHashMapCopyOnWriteArrayList 等。
    • 使用 Atomic 类,如 AtomicIntegerAtomicBoolean 等,它们利用底层的原子操作保证线程安全。
2. 什么是线程安全的单例模式?
  • 回答
    • 饿汉式单例模式
java">public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}
- **懒汉式单例模式(使用 `synchronized`)**:
java">public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
- **双重检查锁定单例模式(DCL)**:
java">public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

这些问题涵盖了 Java 线程的基本概念、创建、同步、通信、池化、并发工具类和线程安全等方面,在面试中较为常见。掌握这些知识将有助于你更好地回答 Java 线程相关的问题,展现你对 Java 多线程编程的理解和应用能力。


http://www.ppmy.cn/embedded/154052.html

相关文章

【2025最新】机器学习类计算机毕设选题80套,适合大数据,人工智能

【2025最新】机器学习类型计算机毕设选题 1-10套 基于Spring Boot的物流管理系统的设计与实现 基于机器学习的虚假招聘信息的分析与预测 基于机器学习的影响数据科学家职业变动因素的分析与预测 基于Spring Boot的历史文物交流平台的设计与实现 基于机器学习的肥胖影响因素的分…

c++ 中的容器 vector、deque 和 list 的区别

表格汇总&#xff1a; 容器存储结构随机访问性能中间插入/删除性能两端插入/删除性能内存管理特点迭代器类型适用场景vector连续存储的动态数组 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n)&#xff08;需要移动元素&#xff09;末尾&#xff1a; O ( 1 ) O(1) O(1)&#xff0c;头部…

如何在C#中使用COM接口

在C中&#xff0c;可以使用CoCreateInstance函数来创建COM接口的实例。 以下教程可以帮助你方便的在C#中实现同样的功能。 方法一、手动生成&#xff08;适用于所有.NET版本&#xff09; 1、确定要使用的COM接口 Windows中很多功能都是通过COM实现的&#xff0c;有时候我们想…

LabVIEW光流跟踪算法

1. 光流跟踪算法的概述 光流&#xff08;Optical Flow&#xff09;是一种图像处理技术&#xff0c;用于估算图像中像素点的运动。通过比较连续帧图像&#xff0c;光流算法可以分析图像中的运动信息&#xff0c;广泛用于目标跟踪、运动检测和视频处理等场景。该示例使用了NI Vi…

Kafka消费者如何优雅下线

一、背景 我们在Kafka消费程序中&#xff0c;可能会调用dubbo接口&#xff0c;也可能会使用线程池&#xff0c;连接池等&#xff0c;但是在服务下线的时候&#xff0c;kafka的消费总是会报错。比如dubbo接口就会抛出异常RpcException: The channel is closed. 这说明kafka还在…

django基于Python的校园个人闲置物品换购平台

Django 基于 Python 的校园个人闲置物品换购平台 一、平台概述 Django 基于 Python 的校园个人闲置物品换购平台是专为校园师生打造的一个便捷、环保且充满活力的线上交易场所。它借助 Django 这一强大的 Python Web 开发框架&#xff0c;整合了校园内丰富的闲置物品资源&…

WEB攻防-通用漏洞_XSS跨站_MXSS_UXSS_FlashXSS_PDFXSS

目录 MXSS攻击 UXSS攻击 FlashXSS PDFXSS MXSS攻击 MXSS&#xff0c;全称“Mutation XSS”&#xff0c;MXSS攻击是一种特殊的XSS攻击类型&#xff0c;简单来说&#xff0c;就是XSS攻击的一种特殊形式&#xff0c;它通过利用网页内容的动态变化或特定条件触发&#xff0c;使…

Golang 并发之 Goroutine

Goroutine 是 Go 编程语言中的一个重要概念。它是 Go 语言实现并发的基础,可以简单地理解为 Go 语言中的轻量级线程。 具体来说,Goroutine 有以下特点: 1.轻量级: Goroutine 的创建和切换都非常快速,只需要几微秒。这与操作系统级别的线程相比要快得多。 2.并发性: Gorout…