ReentrantLock可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全。
用法:
lock(): 加锁, 如果获取不到锁就死等.
trylock(超时时间): 加锁, 如果获取不到锁, 等待一定的时间之后就放弃加锁.
unlock(): 解锁
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// working
} finally {
lock.unlock() //得注意要用finally解锁保证能执行到;避免中间返回了;后面没解锁。 它里面有一个返回值判断的;加锁成功就后面会执行这个解锁代码;否则不执行解锁逻辑。
}
为什么有synchronized还要ReentrantLock?
synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 try lock 的方式等待一段时间就放弃
synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个 true 开启公平锁模式
更强大的唤醒机制:synchronized可以搭配Object 的 wait / notify 实现等待-唤醒。 每次唤醒的是一个随机等待的线程。 ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程。
ThreadPoolExecutor线程池描述;有3个重载方法最少5个参数最多7个参数描述
1:int corePoolsize。你可以理解是一个正式员工;核心线程数
2:maximumPoolsize;最大线程数;正式员工+实习生(临时线程)
3:long keepAliveTime;实习生摸鱼时间;
4:TimeUnit unit,;描述实习生摸鱼的最长时间;超过这个时间就给它开除掉。
5:BlockinBoueue workQueue;描述这个任务队列是阻塞的还是非阻塞的;是有界的还是无界的。无界就是存的任务是无限的。有界的就是存的任务量有限;超过了就执行执行拒绝策略。我们通常设置阻塞无界。
6:threadFactory; 表示线程的创建工厂。用的比较少;一般但是使用默认的线程创建工厂的方法来创建线程。你可以设置创建线程的类型(前台、后台)、优先级、命名。
7:RejectedExecutionHandler;表示指定线程池的拒绝策略。当任务队列满了不能再创建新线程执行此任务就会使用该拒绝策略
创建线程池的方式: Executors是一个线程工厂;Executors 本质上是 ThreadPoolExecutor 类的封装。让我们创建更方便;无需去设置那么多参数。
1:创建核心线程和最大线程一致线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
源代码如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }
2:创建核心线程大小和最大线程数为1的线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
源代码如下:重载方法ThreadFactory 的参数;传入我们自定义的线程创建工厂,如果不传入则会调用默认的线程工厂;线程工厂就是用来设置线程其它特性;比如优先级、命名、类型
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), threadFactory)); }
永远只有一个线程处理任务;适用于所有任务都需要按被提交的顺序来执行的场景,是个单线程的串行。
3:创建核心线程大小为0;最大线程大小Integer.MAX_VALUE线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
源代码如下:默认情况最大线程数为 Integer.MAX_VALUE , 意味着他的线程数几乎可以无限增加。创建的线程都是临时线程,所以他们都会被销毁,这里空闲 线程销毁时间是60秒,也就是说当线程在60秒内没有任务执行则销毁。使用SynchronousQueue 的一个阻塞队列;它只能存一个任务;当一个任务存就来其它任务想存进来就会阻塞等待这个原任务被消费;它的意义不是储存的;而是在与传递的场景;任务消费很快。适用的场景效率就会高;因为没有核心线程一直占用资源;没有任务60秒后就销毁。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue(), threadFactory); }
4:创建线程池定时或者周期性执行任务
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
线程总大小为 3,但核心线程数是 0,线程池会根据任务的需要动态创建线程。
源码如下:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize); }
public static ScheduledExecutorService newScheduledThreadPool( int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); }
利用这个线程池可以设定延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
// 延迟 2 秒后执行任务
scheduledThreadPool.schedule(() -> {
System.out.println("Task executed after 2 seconds");
}, 2, TimeUnit.SECONDS);
// 初始延迟 2 秒,之后每隔 3 秒执行一次任务
//scheduleAtFixedRate 方法用于定期执行任务,无论上一个任务是否执行完成。
scheduledThreadPool.scheduleAtFixedRate(() -> {
System.out.println("Task executed at fixed rate");
}, 2, 3, TimeUnit.SECONDS);
// 初始延迟 2 秒,之后每次任务执行完成后延迟 3 秒再执行下一个任务
//scheduleWithFixedDelay 方法用于定期执行任务,确保上一个任务完成后,间隔固定的时间再执行下一个任务。
scheduledThreadPool.scheduleWithFixedDelay(() -> {
System.out.println("Task executed with fixed delay");
}, 2, 3, TimeUnit.SECONDS);
5:上述都是线程工厂类创建的;封装各种线程池的创建方式。ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定.
import java.util.concurrent.*;
public class CustomThreadPoolExample {
public static void main(String[] args) {
// 1. 核心线程池大小
int corePoolSize = 5;
// 2. 最大线程池大小
int maximumPoolSize = 10;
// 3. 线程空闲时间
long keepAliveTime = 60; // 60 秒
// 4. 空闲时间的单位
TimeUnit unit = TimeUnit.SECONDS;
// 5. 任务队列,这里使用 LinkedBlockingQueue
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
// 6. 线程工厂
ThreadFactory threadFactory = Executors.defaultThreadFactory();
// 7. 拒绝策略,这里使用默认的 AbortPolicy
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
// 创建 ThreadPoolExecutor
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
// 提交任务
executor.submit(() -> {
System.out.println("Task executed by the custom thread pool");
});
// 关闭线程池
executor.shutdown();
}
}
信号量, 用来表示 “可用资源的个数”. 本质上就是一个计数器;Semaphore 的acquire 方法申请资源(–操作), release 方法释放资源(++操作), 原子性可以在多线程环境下直接使用。如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源。
Semaphore semaphore = new Semaphore(4);
semaphore.acquire();//申请资源
semaphore.release();//释放资源
等待任务结束的效果: CountDownLatch latch = new CountDownLatch(10);初始化 10 表示有 10 个任务需要完成。然后你每个任务完成都能调用一下latch.countDown() (原子性–操作)。你在主线程latch.await(); 阻塞等待所有任务执行完毕;相当于计数器为 0 了。