线程池的拒绝策略主要有以下四种:
public class MyRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 这里可以添加你的自定义逻辑
System.out.println("任务被拒绝了");
}
}
// 使用自定义的拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1),
new MyRejectedExecutionHandler());
实际开发中我们会使用哪种拒绝策略呢?
实际开发中我们会使用自定义拒绝策略,因为在自定义拒绝策略灵活好控制,可以在自定义拒绝策略中发送一条通知给消息中心,让消息中心发送告警信息给开发者,这样可以实时的监控线程池的运行状况,并能及时发现和排查问题。
判断线程池任务执行是否完成,有以下几种方法:
?isTerminated():线程池提供了一个原生函数isTerminated()来判断线程池中的任务是否全部完成。当线程池中所有任务都执行完成之后,线程池就会进入终止状态,此时调用isTerminated()方法返回的结果就是true
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
// 提交任务
executor.execute(() -> {
// 任务代码
});
// 关闭线程池
executor.shutdown();
// 判断任务是否完成
if (executor.isTerminated()) {
System.out.println("所有任务已完成");
}
getCompletedTaskCount():我们可以通过判断线程池中计划执行任务和已完成任务,来判断线程池是否已经执行完成。如果相等就代表已经执行完成了
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
// 提交任务
executor.execute(() -> {
// 任务代码
});
// 判断任务是否完成
if (executor.getTaskCount() == executor.getCompletedTaskCount()) {
System.out.println("所有任务已完成");
}
FutureTask():当你提交一个Callable或者RRunnable任务到达线程池的时候,你可以将它包装到一个FutureTask对象中。然后调用FutureTask的get()方法来获取任务的结果,如果任务没有完成,get()方法将会阻塞,直到任务完成并且结果可用。所以,只要get()方法有返回结果,就直到任务是否完成
// 创建一个 Callable 任务
Callable<Integer> task = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// 执行任务,返回结果
return doSomeWork();
}
};
// 创建一个 FutureTask 对象来包装任务
FutureTask<Integer> futureTask = new FutureTask<>(task);
// 提交任务到线程池
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.execute(futureTask);
// 获取任务结果
try {
Integer result = futureTask.get(); // 这里会阻塞,直到任务完成
System.out.println("任务已完成,结果是:" + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
导致线程安全问题的主要问题有以下几点:
解决线程安全问题的手段主要有以下几种:
// 创建一个 ThreadLocal 对象
ThreadLocal<String> threadLocal = new ThreadLocal<>();
// 在每个线程中设置和获取线程本地变量的值
new Thread(() -> {
threadLocal.set("线程1的值");
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
}).start();
new Thread(() -> {
threadLocal.set("线程2的值");
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
}).start();
synchronized是Java中的一个关键字,用于实现线程同步,保证在同一时刻,只有一个线程执行特定的代码块。它的实现主要是依赖于JVM中的一个叫做监视器锁(monitor)的机制,而监视器又是以来操作系统的互斥锁Mutex实现的
当一个线程进入到一个synchronized代码块的时候,它需要获取一个与该代码块关联的monitor对象的所有权。如果获取成功,那么该线程就可以执行synchronized代码块的代码。当代码执行完毕后,线程会释放monitor对象的所有权。这样,其他线程就可以获取monitor对象的所有权,进入到代码块中。
public class SynchronizedToMonitorExample {
private static int count = 0;
public static void main(String[] args) {
synchronized (SynchronizedToMonitorExample.class) {
for (int i = 0; i < 10; i++) {
count++;
System.out.println(count);
}
}
}
}
当我们将上诉代码编译成字节码之后,得到的结果是:
我们可以看出在main方法中多了一对monitorenter和monitorexit的指令,他们的含义是:
由此可知,synchronized是依赖Monitor监视器实现的
synchronized锁的升级流程主要分为以下几步:
在Java中,synchronized锁的自旋次数是不固定的。自旋次数是通过JVM在运行时收集的统计信息,动态调整自旋锁的自旋次数上界,这种机制称为自适应自旋锁。
自旋:就是当一个线程尝试去获取一个已经被其他线程所持有的锁时,它并不是立即阻塞,而是进行一个无意义的循环,看看是否锁已经释放并直接进行竞争上岗步骤,如果竞争不到继续自旋循环,循环过程中线程状态一直处于running状态
虽然自旋锁方式省去了阻塞线程的时间和空间(队列的维护等)开销,但是长时间自旋也是很低效的。所以自旋的次数一般控制在一个范围内,例如10,50等,在超出这个范围后,线程就进入排队队列
synchronized和ReentrantLock都是Java中的同步机制,用于在多线程环境中保护共享资源的访问,但是它们之间存在着一些区别:
ReentrantLock的代码举例:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void incrementCount() {
lock.lock(); // 加锁
try {
count++;
} finally {
lock.unlock(); // 释放锁
}
}
public int getCount() {
return count;
}
}
volatile不能保证线程的安全。volatile的作用有两个:保证内存的可见性和禁止指令重排序。
由于volatile不能保证原子性,而原子性也是导致线程不安全的因素之一,所以volatile不能保证线程安全
volatile 在实际工作中,使用场景如下:
单例模式(双重检查锁定):在单例模式等场景下,volatile可以配合使用双重检查锁定来确保只有一个实例被创建。在获取实例的时候,通过队实力对象进行volatile见你查,确保其他线程能够正确读取已创建实例,如代码:
public class Singleton {
private Singleton() {}
private static volatile Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) { // 1
synchronized (Singleton.class) {
if (instance == null) { // 2
instance = new Singleton(); // 3
}
}
}
return instance;
}
}
定时任务控制标志:在一些定时任务中,可能需要用到标志位来控制任务的启停。通过将标志位声明为volatile,确保在修改标志位的时候能够立即被其他线程所见,并即使停止或者启动相关任务
public class MyThread extends Thread {
private volatile boolean flag = true;
public void stopThread() {
flag = false;
}
@Override
public void run() {
while (flag) {
}
}
}
线程间消息通知:当一个线程需要向另一个线程发送通知的时候,可以使用volatile作为信号量。当一个线程修改了volatile变量的值时,其他线程能够立即看到变化,从而得知有新消息发送
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??