一、线程的基本概念
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(); // 启动线程}
}
- 解释:
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. 使用 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);}
}
- 解释:
三、线程的生命周期
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;}}
}
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. 使用 ExecutorService
和 ThreadPoolExecutor
:
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();}
}
- 解释:
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();}
}
七、线程的调度和优先级
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. 线程调度:
八、线程的中断
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();}
}
- 解释:
九、并发工具类
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");}
}
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();}
}
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 线程在不同场景下的使用方式,包括并发处理、资源下载、生产者-消费者模式、并行计算和定时任务。在实际开发中,合理使用线程可以显著提高程序的性能和响应速度,但需要注意线程安全和资源竞争等问题。根据具体的业务需求和性能要求,可以灵活运用不同的线程创建和管理方式。
在上述示例中,不同的场景使用了不同的线程创建和管理方式,包括 ExecutorService
、ThreadPoolExecutor
、Runnable
、Callable
和 Future
等,你可以根据自己的需求进行调整和扩展。
以下是 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):线程完成任务或出现异常而终止。
- 新建(New):创建
2. 如何让一个线程暂停和恢复执行?
- 回答:
三、线程同步类问题
1. 解释 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. 什么是死锁?如何避免死锁?
- 回答:
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()
的使用方法。
- 回答:
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. 如何创建和使用线程池?
- 回答:
- 可以使用
ExecutorService
和ThreadPoolExecutor
创建线程池,例如:
- 可以使用
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
的作用和使用方法。
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");}
}
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();}
}
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. 如何保证线程安全?
- 回答:
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 多线程编程的理解和应用能力。