线程相关
创建线程的几种方式
四种方式
- 实例化Thread
- 实现Runnable
- Callable与FutureTask结合使用
- 线程池
详细实现参考:https://editor.csdn.net/md/?articleId=131819983
Runnable和Callable的区别
- 最大的区别,runnable没有返回值,而实现callable接口的任务线程能返回执行结果
- callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出
如何启动一个线程,调用Start和run方法的区别
启动线程:
调用线程的thread.start()方法
start和run方法的区别:
实现多线程最基本的两种方式:
(1) java.lang.Thread 接口
(2) java.lang.Runnable 接口
其中Thread也是实现了Runnable接口
Runnable接口源码
@FunctionalInterface
public interface Runnable {
/**
* 当使用实现接口Runnable的对象来创建线程时,启动线程会导致在单独执行的线程中调用对象的run方法。方法运行的一般约定是它可以执行任何操作。
*/
public abstract void run();
}
所以创建多线程都需要实现run()方法,是多线程真正执行的主方法
而start()方法则是Thread类的方法,用来异步启动一个线程,然后主线程立刻返回,该启动的线程不会马上运行,会放到等待队列中等待CPU调度,只有线程真正被CPU调用时才会调用run()方法执行,所以start()方法只是标识线程为就绪状态的一个附加方法,其中start()是一个本地native()方法
直接调用线程的run()方法 ,相当于调用了一个普通的同步方法。
线程有哪几种状态以及各种状态之间的转换
详解参考:https://editor.csdn.net/md/?articleId=131829207
线程相关的基本方法
sleep和wait的区别
-
使用限制
sleep(long)在线程的任何地方都能使用、 wait() 、wait(long) 需要配合synchronized使用 wait() 唤醒需要配合notify() 或者 notifyAll() 使用
-
不同的类
sleep(long) 在 Thread类中,wait()、wait(long)在Object类中
-
使用场景
sleep() 一般用户当前线程休眠,wait() 用于多线程之间的通信
-
释放锁
wait()会释放获取到的锁并允许其他线程获取锁并继续执行,而sleep() 不会
-
线程切换(CPU资源)
sleep()会让出CUP时间片,强制上下文切换 wait()会让出CPU时间片,但是不会强制上下文切换
yield和sleep的区别
相同点:
1. 暂停当前线程
2. 如果获取到锁资源,都不会释放锁
3. 都会让出CPU时间片资源
不同点:
1. sleep() 可以指定休眠时间,而yield则依赖CPU的时间片划分
2. yield不能被中断,而sleep则可以被中断(需要捕获中断异常)
3. sleep() 让出CPU时间片给其他线程运行的机会不会考虑优先级,
而yield() 只会给相同优先级或者更高优先级的线程运行的机会。
5. sleep()方法声明抛出InterruptedException异常,yield没有
wait、notify、notifyAll()并不是Thread类中的方法,而是Object类中的,为什么呢?
因为sleep(long)是让当前线程休眠,
不涉及到对象类,也不需要获取对象的锁,
所以是线程类方法。wait()是让获得对象锁的线程实现等待,
前提是获取到对象的锁,所以是类的方法。notify、notifyAll()与wait配套使用,
所以也是Object类中的方法。
为什么不推荐使用Stop停止线程
stop()会释放锁并强制终止线程,造成执行一半的线程终止。
假如业务场景:
一个线程正在处理一个复制的业务流程,突然间线程被调用Stop而意外终止,这个业务数据就可能出现数据不一致的问题。
正因为stop方法太过暴力,所以一般不推荐使用,除非使用stop方法不会对业务带来影响
如何优化的终止一个线程
标志位
如何实现
添加一个volatile变量,判断这个变量在某个值的时候退出循环。
public class TaskThread extends Thread {
// 为什么需要使用volatile 关键字 参考volatile 详解
private static volatile boolean stop = false;
@Override
publlic void run() {
while(!stop) {
// 线程逻辑
}
}
public static void main(String[] args) throws InterruptedException {
TaskThread thread = new TaskThread();
thread.start();
TimeUnit.SECONDS.sleep(3);
stop = true;
}
}
如何实现线程顺序执行
详解参考:https://blog.csdn.net/ChrisLee2013/article/details/131841698
synchronized同步锁有哪几种用法
实例锁
- 同步普通方法
- 同步this实例
类锁 - 同步静态方法
- 同步类
对象锁 - 同步对象实例
类锁和实例锁不会相互阻塞,但是相同的类锁,相同的当前实例锁,相同的对象锁会相互阻塞
线程池相关
线程池说明参考:https://blog.csdn.net/ChrisLee2013/article/details/131818811
为什么使用线程池
线程池的分类
核心参数
线程池的工作流程
线程池的原理
拒绝策略
如何关闭线程池
线程池中阻塞队列的作用?为什么是先添加列队而不是先创建最大线程?
线程池中线程复用原理
线程池submit和execute的区别
谈谈多线程中的CompletionService
谈谈多线程中的ExecutorCompletionService
Java实现异步编程的方案
线程池中的线程抛出了异常,如何处理
- 在线程具体的处理逻辑中使用try-catch异常块捕获异常处理异常。
- 自定义一个ThreadFactory线程工厂,然后设置Thread线程多的
Thread.setUncaughtExceptionHandler
方法捕获异常
代码实现:
/**
* @Author chenwenlin
* @Date 2023-07-20 23:02
*/
public class UncaughtExceptionHandler {
public static void main(String[] args) {
// 创建线程工厂
ThreadFactory threadFactory = (Runnable r) -> {
Thread thread = new Thread(r);
thread.setUncaughtExceptionHandler((Thread thread1, Throwable e) -> {
System.out.println("error......");
});
return thread;
};
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(100), threadFactory);
threadPoolExecutor.execute(new Task());
}
static class Task implements Runnable {
@Override
public void run() {
int i = 1 / 0;
System.out.println(i);
}
}
}
CAS
什么是CAS
乐观锁
即:比较互换
CAS缺点
- CAS是基于比较再交换,如果失败循环次数过多,导致CPU的消耗比较大。
- 只能保证一个变量的原子操作,无法保证非原子操作结果正确。
- 会出现ABA问题。
ABA问题解决
原子引用
有了AtomicInterger为什么还要搞出LongAdder
AtomicInterger如果并发数很高的时候,会造成过多的没有必要的循环,会影响CPU的性能。所以JDK8增加了一个LongAdder类。