三大辅助类
CountDownLatch
CountDownLatch
CountDownLatch 是 Java 中的一个同步辅助类,它允许一个或多个线程等待其他线程完成操作。
构造函数
CountDownLatch(int count)
主要方法
void await() throws InterruptedException:使当前线程等待,直到计数器减为0。boolean await(long timeout, TimeUnit unit) throws InterruptedException:使当前线程等待,直到计数器减为0或者超时。void countDown():减少计数器的值。long getCount():获取当前计数器的值。
示例代码
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
Runnable task = () -> {
try {
Thread.sleep(1000);
System.out.println("Task completed");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
for (int i = 0; i < 3; i++) {
new Thread(task).start();
}
latch.await();
System.out.println("All tasks completed");
}
}
Cyclicbarrierr
CyclicBarrier
CyclicBarrier 是 Java 中的一个同步辅助类,允许一组线程相互等待,直到所有线程都到达某个共同的屏障点。它可以用于实现多线程的协作,确保所有线程在某个时刻一起执行。
构造函数
CyclicBarrier(int parties):创建一个新的CyclicBarrier,指定参与者的数量。CyclicBarrier(int parties, Runnable barrierAction):创建一个新的CyclicBarrier,指定参与者的数量,并在所有参与者到达屏障时执行一个指定的操作。
主要方法
int await() throws InterruptedException, BrokenBarrierException:使当前线程等待,直到所有参与者都到达屏障点。int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException:使当前线程等待,直到所有参与者到达屏障点或超时。int getParties():获取参与者的数量。int getNumberWaiting():获取当前正在等待的参与者数量。boolean isBroken():检查屏障是否处于损坏状态。void reset():重置屏障,使所有参与者回到初始状态。
示例代码
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
// 创建一个CyclicBarrier,指定参与者数量为3,并在所有线程到达时执行的操作
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有任务已完成,继续执行主线程"));
Runnable task = () -> {
try {
// 模拟任务执行
Thread.sleep((long) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName() + " 完成任务");
// 等待其他线程到达
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
};
// 启动3个线程
for (int i = 0; i < 3; i++) {
new Thread(task).start();
}
}
}
代码解析
CyclicBarrier的创建:在示例中,我们创建了一个
CyclicBarrier,它需要3个参与者,并在所有线程到达时执行一个打印操作。任务执行:每个线程会随机睡眠一段时间,模拟任务的执行。
等待其他线程:每个线程在完成任务后调用
barrier.await(),等待其他线程到达屏障。主线程继续执行:当所有线程都到达屏障时,执行指定的操作,输出“所有任务已完成,继续执行主线程”。
Semaphore
Semaphore 是 Java 中的一个同步辅助类,用于控制同时访问特定资源的线程数量。它维护一定数量的许可,线程需要获取许可才能执行,执行完毕后释放许可。
构造函数
Semaphore(int permits): 创建一个具有给定许可数的Semaphore。
主要方法
void acquire() throws InterruptedException: 获取一个许可,如果没有许可可用,则阻塞直到有许可为止。void release(): 释放一个许可,将其返回给Semaphore。int availablePermits(): 返回当前可用的许可数。boolean tryAcquire(): 尝试获取一个许可,如果成功立即返回true,否则返回false。
示例代码
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2); // 创建一个许可数为2的Semaphore
Runnable task = () -> {
try {
semaphore.acquire(); // 获取许可
System.out.println(Thread.currentThread().getName() + " 获取许可");
Thread.sleep(2000); // 模拟任务执行
System.out.println(Thread.currentThread().getName() + " 释放许可");
semaphore.release(); // 释放许可
} catch (InterruptedException e) {
e.printStackTrace();
}
};
// 启动5个线程
for (int i = 0; i < 5; i++) {
new Thread(task).start();
}
}
}
代码解析
Semaphore的创建:在示例中,我们创建了一个许可数为2的
Semaphore,表示最多有2个线程可以同时执行任务。任务执行:每个线程在执行任务前先尝试获取许可,如果许可数已满,则会阻塞直到有许可可用。
释放许可:每个线程执行完任务后会释放许可,让其他线程可以获取许可继续执行。
控制并发数量:通过
Semaphore,我们可以灵活控制同时执行任务的线程数量,实现资源的合理利用。
读写锁ReadWriteLock
读写锁 ReadWriteLock
读写锁(ReadWriteLock)是一种特殊的锁机制,用于控制对共享资源的访问。它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。读写锁可以提高并发性能,适用于读多写少的场景。
构造函数
ReadWriteLock readWriteLock = new ReentrantReadWriteLock(): 创建一个读写锁实例。
主要方法
Lock readLock = readWriteLock.readLock(): 获取读锁实例。Lock writeLock = readWriteLock.writeLock(): 获取写锁实例。void lock(): 获取锁,如果锁不可用则阻塞。void unlock(): 释放锁。
示例代码
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private static int count = 0;
private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) {
Runnable readTask = () -> {
readWriteLock.readLock().lock(); // 获取读锁
try {
System.out.println("Read value: " + count);
} finally {
readWriteLock.readLock().unlock(); // 释放读锁
}
};
Runnable writeTask = () -> {
readWriteLock.writeLock().lock(); // 获取写锁
try {
count++; // 修改共享资源
System.out.println("Write value: " + count);
} finally {
readWriteLock.writeLock().unlock(); // 释放写锁
}
};
// 启动5个读线程
for (int i = 0; i < 5; i++) {
new Thread(readTask).start();
}
// 启动2个写线程
for (int i = 0; i < 2; i++) {
new Thread(writeTask).start();
}
}
}
代码解析
创建一个
ReadWriteLock实例。定义一个共享资源
count,初始值为0。定义一个读任务
readTask,获取读锁,读取共享资源的值,并释放读锁。定义一个写任务
writeTask,获取写锁,修改共享资源的值,并释放写锁。启动5个读线程,每个线程读取共享资源的值。
启动2个写线程,每个线程修改共享资源的值。
通过读写锁,我们可以实现多个线程同时读取共享资源,提高并发性能。同时,读写锁保证了写操作的原子性,只允许一个线程写入共享资源,避免了数据不一致的问题。
独占锁和共享锁
独占锁(Exclusive Lock)
独占锁是一种锁机制,也称为排他锁,它允许只有一个线程对共享资源进行访问,其他线程必须等待该线程释放锁之后才能访问共享资源。独占锁适用于需要独占资源进行操作的场景,例如写操作。
示例代码
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ExclusiveLockExample {
private static int count = 0;
private static Lock exclusiveLock = new ReentrantLock();
public static void main(String[] args) {
Runnable task = () -> {
exclusiveLock.lock(); // 获取独占锁
try {
count++; // 修改共享资源
System.out.println("Modified value: " + count);
} finally {
exclusiveLock.unlock(); // 释放独占锁
}
};
// 启动3个线程
for (int i = 0; i < 3; i++) {
new Thread(task).start();
}
}
}
代码解析
创建一个
ReentrantLock实例作为独占锁。定义一个共享资源
count,初始值为0。定义一个任务
task,获取独占锁,修改共享资源的值,并释放独占锁。启动3个线程,每个线程对共享资源进行修改操作。
通过独占锁,我们可以确保在任意时刻只有一个线程可以修改共享资源,避免了多个线程同时修改导致的数据不一致问题。
共享锁(Shared Lock)
共享锁是一种锁机制,它允许多个线程同时对共享资源进行读取操作,但在有线程对资源进行写操作时,不允许其他线程进行读取操作。共享锁适用于读多写少的场景,可以提高并发性能。
示例代码
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class SharedLockExample {
private static int count = 0;
private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) {
Runnable readTask = () -> {
readWriteLock.readLock().lock(); // 获取共享读锁
try {
System.out.println("Read value: " + count);
} finally {
readWriteLock.readLock().unlock(); // 释放共享读锁
}
};
Runnable writeTask = () -> {
readWriteLock.writeLock().lock(); // 获取独占写锁
try {
count++; // 修改共享资源
System.out.println("Write value: " + count);
} finally {
readWriteLock.writeLock().unlock(); // 释放独占写锁
}
};
// 启动3个读线程
for (int i = 0; i < 3; i++) {
new Thread(readTask).start();
}
// 启动2个写线程
for (int i = 0; i < 2; i++) {
new Thread(writeTask).start();
}
}
}
代码解析
创建一个
ReentrantReadWriteLock实例作为读写锁。定义一个共享资源
count,初始值为0。定义一个读任务
readTask,获取共享读锁,读取共享资源的值,并释放共享读锁。定义一个写任务
writeTask,获取独占写锁,修改共享资源的值,并释放独占写锁。启动3个读线程,每个线程对共享资源进行读取操作。
启动2个写线程,每个线程对共享资源进行修改操作。
通过共享锁,我们可以实现多个线程同时读取共享资源,提高并发性能,同时保证了写操作的原子性,避免了数据不一致的问题。
阻塞队列
阻塞队列(Blocking Queue)是一种特殊的队列,它支持在队列为空时进行获取操作的线程阻塞,以及在队列已满时进行插入操作的线程阻塞。阻塞队列常用于生产者-消费者模式中,能够有效地协调生产者和消费者的速度,实现线程间的同步。
阻塞队列的主要 API 包括:
put(E e): 将元素放入队列,如果队列已满则阻塞。take(): 从队列中取出元素,如果队列为空则阻塞。offer(E e, long timeout, TimeUnit unit): 将元素放入队列,如果队列已满则等待指定时间后返回。poll(long timeout, TimeUnit unit): 从队列中取出元素,如果队列为空则等待指定时间后返回。add(E e): 将元素放入队列,如果队列已满则抛出异常。remove(): 从队列中取出元素,如果队列为空则抛出异常。element(): 获取队列头部的元素,如果队列为空则抛出异常。peek(): 获取队列头部的元素,如果队列为空则返回 null。offer(): 将元素放入队列,有返回值不抛出异常,如果队列已满则返回falsepoll(): 从队列中取出元素,有返回值不抛出异常,如果队列为空则返回 null
示例代码如下:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5); // 创建一个容量为5的阻塞队列
Runnable producer = () -> {
try {
for (int i = 1; i <= 10; i++) {
queue.put(i); // 将元素放入队列,如果队列已满则阻塞
System.out.println("Produced: " + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable consumer = () -> {
try {
for (int i = 1; i <= 10; i++) {
int value = queue.take(); // 从队列中取出元素,如果队列为空则阻塞
System.out.println("Consumed: " + value);
Thread.sleep(1500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
new Thread(producer).start(); // 启动生产者线程
new Thread(consumer).start(); // 启动消费者线程
}
}
在示例代码中,我们使用 ArrayBlockingQueue 创建了一个容量为5的阻塞队列。生产者线程不断向队列中放入元素,如果队列已满则会阻塞;消费者线程不断从队列中取出元素,如果队列为空则会阻塞。这样就实现了生产者和消费者之间的协调。
SynchronousQueue 同步队列
SynchronousQueue
SynchronousQueue 是 Java 中的一个特殊的阻塞队列,它的容量为0,即每个插入操作必须等待一个相应的删除操作,反之亦然。它主要用于线程间的数据交换,例如生产者-消费者模式中的数据传递。
构造函数
SynchronousQueue(): 创建一个不存储元素的SynchronousQueue。
主要方法
put(E e): 将指定的元素插入此队列,如果没有消费者线程等待接收,则阻塞等待。take(): 从队列中取出元素,如果没有生产者线程等待传递元素,则阻塞等待。offer(E e, long timeout, TimeUnit unit): 将指定的元素插入此队列,如果没有消费者线程等待接收,则等待指定时间后返回。poll(long timeout, TimeUnit unit): 从队列中取出元素,如果没有生产者线程等待传递元素,则等待指定时间后返回。
示例代码
import java.util.concurrent.SynchronousQueue;
public class SynchronousQueueExample {
public static void main(String[] args) {
SynchronousQueue<Integer> queue = new SynchronousQueue<>();
Runnable producer = () -> {
try {
queue.put(1); // 将元素放入队列,如果没有消费者线程等待接收,则阻塞等待
System.out.println("Produced: 1");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable consumer = () -> {
try {
int value = queue.take(); // 从队列中取出元素,如果没有生产者线程等待传递元素,则阻塞等待
System.out.println("Consumed: " + value);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
new Thread(producer).start(); // 启动生产者线程
new Thread(consumer).start(); // 启动消费者线程
}
}
代码解析
创建一个
SynchronousQueue实例,用于生产者和消费者线程之间的数据交换。定义一个生产者任务
producer,将元素放入队列,如果没有消费者线程等待接收,则阻塞等待。定义一个消费者任务
consumer,从队列中取出元素,如果没有生产者线程等待传递元素,则阻塞等待。启动生产者线程和消费者线程,进行数据交换操作。
通过 SynchronousQueue,我们可以实现线程间的数据交换,确保生产者和消费者线程之间的同步。
线程池
池化技术
池化技术概述
池化技术是一种常见的资源管理技术,通过预先创建一定数量的资源池,在需要时从池中获取资源,并在使用完毕后将资源归还到池中,以提高资源的重复利用率和系统性能。池化技术通常应用于数据库连接池、线程池、对象池等场景。
池化技术优势
资源重复利用:池化技术可以避免频繁地创建和销毁资源,提高资源的重复利用率,减少资源的浪费。
性能优化:通过池化技术,可以减少资源的创建和销毁开销,降低系统负担,提高系统性能和响应速度。
资源控制:池化技术可以限制资源的数量,防止资源过度消耗,避免系统资源耗尽导致系统崩溃。
提高并发能力:池化技术可以有效管理资源的并发访问,避免资源竞争和线程阻塞,提高系统的并发能力。
池化技术应用场景
数据库连接池:在数据库访问中,通过数据库连接池管理数据库连接,避免频繁地创建和关闭数据库连接,提高数据库访问效率。
线程池:通过线程池管理线程资源,控制并发线程数量,避免线程频繁创建和销毁带来的性能开销。
对象池:管理对象的创建和销毁,例如连接对象、缓存对象等,提高对象的重复利用率,降低系统开销。
连接池:管理网络连接资源,如HTTP连接池、FTP连接池等,减少连接建立和断开的开销,提高网络通信效率。
资源池:管理各种资源的池化,如文件资源、内存资源等,提高资源的利用率和系统性能。
池化技术实现方式
对象池:预先创建一定数量的对象,通过对象池管理对象的获取和释放。
连接池:维护一定数量的连接,通过连接池管理连接的获取和释放。
线程池:管理一定数量的线程,通过线程池管理线程的执行和复用。
缓存池:管理缓存对象,通过缓存池管理缓存的存储和访问。
资源池:管理各种资源的池化,通过资源池管理资源的获取和释放。
池化技术实现步骤
初始化池:创建一定数量的资源,加入到池中。
资源获取:从池中获取资源,如果池中无可用资源,则等待或创建新资源。
资源使用:使用获取到的资源进行操作。
资源归还:在资源使用完毕后,将资源归还到池中,以便其他线程继续使用。
资源销毁:在池不再需要时,销毁池中的资源,释放资源占用的内存。
总结
池化技术是一种重要的资源管理技术,通过预先创建资源池,有效管理资源的获取和释放,提高资源的重复利用率和系统性能。在实际开发中,合理应用池化技术可以优化系统架构,提升系统的稳定性和性能。
Executors 三大方法
newFixedThreadPool
newFixedThreadPool 方法返回一个固定大小的线程池,该线程池中的线程数量始终保持不变。当有新的任务提交时,如果线程池中有空闲线程,则立即执行;如果线程池中没有空闲线程,则任务将被放入任务队列中等待执行。
方法签名
public static ExecutorService newFixedThreadPool(int nThreads)
示例代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3); // 创建一个固定大小为3的线程池
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println("Thread " + Thread.currentThread().getName() + " is executing task");
});
}
executor.shutdown(); // 关闭线程池
}
}
newCachedThreadPool
newCachedThreadPool 方法返回一个可根据实际情况调整线程数量的线程池。当有新的任务提交时,如果线程池中有空闲线程,则立即执行;如果线程池中没有空闲线程,则创建新的线程执行任务。当线程空闲时间超过60秒,将被回收。
方法签名
public static ExecutorService newCachedThreadPool()
示例代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool(); // 创建一个可根据实际情况调整线程数量的线程池
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println("Thread " + Thread.currentThread().getName() + " is executing task");
});
}
executor.shutdown(); // 关闭线程池
}
}
newSingleThreadExecutor
newSingleThreadExecutor 方法返回一个只有一个线程的线程池,保证所有任务按照指定顺序执行。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
方法签名
public static ExecutorService newSingleThreadExecutor()
示例代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadExecutorExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor(); // 创建一个只有一个线程的线程池
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println("Thread " + Thread.currentThread().getName() + " is executing task");
});
}
executor.shutdown(); // 关闭线程池
}
}
通过 Executors 类提供的这三种线程池,可以方便地创建不同类型的线程池,满足不同的业务需求。
ThreadPoolExecutor
ThreadPoolExecutor 是 Java 中用于管理线程池的类,通过它可以方便地创建和管理线程池,控制线程的数量、任务的执行以及线程池的行为。ThreadPoolExecutor 提供了丰富的参数和方法,可以根据需求灵活地配置线程池的各项属性。
七大参数
corePoolSize:核心线程数,线程池中始终保持的线程数量,即使线程处于空闲状态。当提交任务时,如果当前线程数小于
corePoolSize,则会创建新线程来处理任务,即使有空闲线程可用。如果线程数大于corePoolSize,则任务会被放入任务队列等待执行。maximumPoolSize:最大线程数,线程池中允许的最大线程数量。当任务队列已满且当前线程数小于
maximumPoolSize时,会创建新线程来处理任务。超过最大线程数的任务会根据拒绝策略进行处理。keepAliveTime:线程空闲时间,当线程数大于
corePoolSize时,多余的空闲线程会在指定时间内被回收,直到线程数等于corePoolSize。这样可以节省系统资源。unit:空闲时间的单位,通常与
keepAliveTime一起使用,表示空闲时间的单位,如TimeUnit.SECONDS。workQueue:任务队列,用于存储等待执行的任务。
ThreadPoolExecutor提供了多种任务队列类型,如ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等,可以根据需求选择合适的队列类型。threadFactory:线程工厂,用于创建新线程。可以自定义线程工厂来设置线程的名称、优先级等属性。
handler:拒绝策略,当任务无法被执行时的处理策略。常见的拒绝策略包括
AbortPolicy(默认策略,抛出异常)、CallerRunsPolicy(由调用线程执行任务)、DiscardPolicy(丢弃任务)和DiscardOldestPolicy(丢弃队列头部任务)。
通过合理配置这七大参数,可以根据实际需求创建出高效、稳定的线程池,提高系统的并发处理能力。
示例代码
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorExample {
public static void main(String[] args) {
int corePoolSize = 2;
int maximumPoolSize = 5;
long keepAliveTime = 5000;
TimeUnit unit = TimeUnit.MILLISECONDS;
ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
// 提交任务
for (int i = 0; i < 10; i++) {
executor.execute(() -> System.out.println("Task executed by " + Thread.currentThread().getName()));
}
// 关闭线程池
executor.shutdown();
}
}
在上面的示例中,我们创建了一个 ThreadPoolExecutor,设置了核心线程数为2,最大线程数为5,线程空闲时间为5秒,任务队列为容量为10的 ArrayBlockingQueue。然后提交了10个任务给线程池执行,并最终关闭了线程池。
四种拒绝策略
在使用线程池时,当任务数量超过线程池的处理能力时,可能会出现拒绝任务的情况。Java 提供了四种拒绝策略来处理这种情况,具体如下:
1. AbortPolicy
描述
这是默认的拒绝策略。当线程池无法处理新任务时,它会抛出 RejectedExecutionException。这种策略适用于对任务处理有严格要求的场景。
示例代码
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class AbortPolicyExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(2), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println("Task executed by " + Thread.currentThread().getName());
});
}
}
}
2. CallerRunsPolicy
描述
当线程池无法处理新任务时,调用者线程会直接执行该任务。这样可以减轻线程池的压力,适用于对任务处理有一定灵活性的场景。
示例代码
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CallerRunsPolicyExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(2), new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println("Task executed by " + Thread.currentThread().getName());
});
}
}
}
3. DiscardPolicy
描述
当线程池无法处理新任务时,直接丢弃该任务,不抛出异常。这种策略适用于对任务丢失不敏感的场景。
示例代码
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class DiscardPolicyExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(2), new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println("Task executed by " + Thread.currentThread().getName());
});
}
}
}
4. DiscardOldestPolicy
描述
当线程池无法处理新任务时,丢弃最旧的任务,并尝试提交当前任务。这种策略适用于希望保留最新任务的场景。
示例代码
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class DiscardOldestPolicyExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(2), new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println("Task executed by " + Thread.currentThread().getName());
});
}
}
}
总结
选择合适的拒绝策略对于线程池的性能和稳定性至关重要。根据具体的业务需求和场景,合理配置拒绝策略可以有效地管理任务的执行和资源的利用。
线程池选择建议
在 Java 中,线程池是一种重要的并发编程工具,它可以管理和重用线程,提高程序的性能和稳定性。在选择线程池时,通常推荐使用 ThreadPoolExecutor 而不是 Executors 工厂类来创建线程池,主要有以下几个原因:
灵活性:
ThreadPoolExecutor提供了更多的参数配置选项,可以根据实际需求进行灵活调整,如核心线程数、最大线程数、线程存活时间、任务队列类型等,而Executors工厂类提供的方法具有固定的配置,无法满足所有场景的需求。避免资源耗尽:
Executors工厂类中的一些静态方法会使用默认的配置,如newFixedThreadPool会使用LinkedBlockingQueue作为任务队列,如果任务过多,可能会导致内存溢出。而使用ThreadPoolExecutor可以根据实际情况选择合适的队列类型,避免资源耗尽问题。任务拒绝策略:
ThreadPoolExecutor允许自定义任务拒绝策略,当线程池无法处理新任务时的处理方式,如抛出异常、丢弃任务、阻塞调用者等。而Executors工厂类提供的方法中无法直接指定任务拒绝策略。线程池扩展性:通过继承
ThreadPoolExecutor类,可以实现自定义的线程池,满足特定需求。而Executors工厂类提供的方法无法实现这种扩展性。
阿里java手册解释:
FixedThreadPoolExecutor和SingleThreadExecutor可能会因为大量请求堆积而造成OOM
CacheThreadPoolExecutor 可能会因为大量创建线程而造成OOM
Comments