多线程并发相关知识点
文章目录1、wait/sleep的区别:2、synchronized出现异常会释放锁?3、synchronized和Lock的区别?4、Runnable和Callable的区别?5、为什么内部类不能访问非final的局部变量?6、队列方法的区别?7、线程池详解8、自定义线程池时,我们如何确定线程池的最大线程数量?9、线程池的shutdown和shutdownNow方法的区别?https://thi
文章目录
-
-
- 1、wait/sleep的区别:
- 2、synchronized出现异常会释放锁?
- 3、synchronized和Lock的区别?
- 4、Runnable和Callable的区别?
- 5、为什么内部类不能访问非final的局部变量?
- 6、阻塞队列方法的区别?
- 7、线程池详解
- 8、自定义线程池时,我们如何确定线程池的最大线程数量?
- 9、volatile如何保证可见性和有序性?
- 10、synchronized详解
- 11、JUC三大辅助类
- 12、线程的状态
- 13、产生死锁的四个必要条件:
- 11、练习题
- 12、基于ReentrantLock详解AQS
- 13、CompleteFuture详解
- 14、ThraedLocal
-
https://thinkwon.blog.csdn.net/article/details/104863992
1、wait/sleep的区别:
- 来自不同的类:
wait => Object
sleep => Thread - 关于锁的释放:
wait会释放锁,sleep不会释放锁 - 使用范围:
wait必须在同步代码块中使用,而sleep可以在任何地方使用 - 是否需要捕获异常:
wait需要捕获异常,sleep需要捕获异常 - 同:都是让线程进入等待状态。
2、synchronized出现异常会释放锁?
会释放。
3、synchronized和Lock的区别?
- synchronized是关键字,Lock是接口;
- synchronized是不可中断的,Lock是可中断的,也可以是不可中断的;
- synchronized会自动释放锁,Lock锁需要手动释放锁;
- synchronized是非公平锁,Lock锁可以是公平锁也可以是非公平锁‘;
- Lock可以知道线程有没有获取锁,而synchronized不能;
- synchronized可以锁代码块和方法,Lock锁只能锁代码块;
4、Runnable和Callable的区别?
- Callable有返回值,而Runnable没有返回值
- Callable可以抛出异常,而Runnable不能抛出异常
- Callable的方法是call(),而Runnable的方法是run()
为什么Runnable接口的run方法不能抛出异常,而必须捕获呢?
因为Runnable接口中的run方法没有抛出异常,所以子类在重写run方法时,不能抛出异常。
5、为什么内部类不能访问非final的局部变量?
局部变量的生命周期:当该方法被调用时,该方法中的局部变量在栈中被创建,当方法调用结束时,退栈,这些局部变量全部死亡。而内部类对象生命周期与其它类对象一样:自创建一个匿名内部类对象,系统为该对象分配内存,直到没有引用变量指向分配给该对象的内存,它才有可能会死亡(被JVM垃圾回收)。所以完全可能出现的一种情况是:成员方法已调用结束,局部变量已死亡,但匿名内部类的对象仍然活着。
1.局部变量和匿名内部类的生命周期不一致,所以匿名内部类会复制一份到匿名内部类中
2.之所以使用强制使用final修饰局部变量,是为了保证内外局部变量之间的数据一致性,至于使不使用final不重要,重要的是局部变量只有一次赋值,出现二次赋值立马就报错。
6、阻塞队列方法的区别?

阻塞队列的使用:
| 操作 | 抛出异常 | 有返回值(true/false) | 阻塞等待 | 超时等待 |
|---|---|---|---|---|
| 添加 | add() | offer() | put() | offer(E e, long timeout, TimeUnit unit) |
| 移除 | remove() | poll() | take() | poll(long timeout, TimeUnit unit) |
| 获取队首元素 | element() | peek() |
当使用add()往一个满的队列中加入元素时,会抛出如下的异常:
抛出异常:
java.lang.IllegalStateException: Queue full
当使用remove()移除一个空队列的元素时,或者使用element()获取一个空队列的队首元素时,会抛出如下异常:
队列为空:
java.util.NoSuchElementException
使用offer()往队列添加元素,添加成功返回true,队满添加失败,返回false。
使用poll()出队元素时,或者peek()获取队首元素时,如果元素存在,则返回元素,否则返回null。
当使用put()往队列添加元素时,如果队列不满则添加成功,否则会阻塞(一直阻塞)。
当使用take()出队元素时,如果队列有元素则出队,并返回该元素,如果队列为空,调用该方法则会阻塞(一直阻塞)。
offer(E e, long timeout, TimeUnit unit) 可以指定超时等待的时间,不会一直等待下去。
poll(long timeout, TimeUnit unit) 也一样。
7、线程池详解
https://blog.csdn.net/Linging_24/article/details/109401629
8、自定义线程池时,我们如何确定线程池的最大线程数量?
《Java并发编程实战》公式:最大线程数 = (CPU核心数 * CPU利用率) / (1 - 阻塞系数)。
阻塞系数:线程等待的时间占比 = 线程平均等待时间 / 线程平均等待时间 + 线程占用CPU时间
早期经验法则:
-
CPU密集型:
套入公式,对于CPU密集型任务,阻塞系数接近0,所以最大线程数 ≈ CPU核心数
Runtime.getRuntime().availableProcessors(); //获取CPU最大并行线程的数量。 -
IO密集型:当时Web应用普遍I/O等待时间约为50%
套入公式,最大线程数 = 2 * CPU核心数
-
混合型:
介于cpu核心数和2*cpu核心数之间
现代演进实践:
- 计算密集型:线程数 = CPU核心数 + 1(或直接等于核心数)
- I/O密集型:使用上述公式作为起点,通过压测调整
- 混合型:通常取 2 × CPU核心数 到 公式计算值 之间
最大线程数调整流程:理论 => 压测 => 监控 + 动态调整
9、volatile如何保证可见性和有序性?
https://blog.csdn.net/bfj11/article/details/123949405
https://blog.csdn.net/jyxmust/article/details/76946283
有序性:lock指令前缀 + 内存屏障
可见性:内存屏障
内存屏障的两个作用:
- 禁止指令重排序
- 读写强刷,立即可见
10、synchronized详解
https://blog.csdn.net/Linging_24/article/details/110541592
11、JUC三大辅助类
- CountDownLatch:实现让一个线程等待其他线程执行完毕后再执行。
- CyclicBarrier:实现让一组线程等待至某一状态,再全部同时执行。
- Semaphore:Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量。
12、线程的状态

-
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。 -
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。 -
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。 -
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:- 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
-
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
13、产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
11、练习题
- 三个线程t1、t2、t3。确保三个线程,t1执行完后t2执行,t2执行完后t3执行。执行顺序:t1->t2->t3
1.Semaphore实现,初始凭证0
2.Lock + condition实现
3.synchronized + wait + notifyAll实现
4.join
- 三个线程t1、t2、t3。确保三个线程,t1,t2执行完,t3再执行
1.CountDownLatch实现
2.join实现
3.synchronized + wait + notifyAll实现
4.Lock + condition实现
- 生产者消费者问题
1.synchronized + wait + notifyAll实现
2.Lock + condition实现
- 多线程循环顺序打印:ABABABABAB,线程A打印A,线程B打印B。
1.synchronized + wait + notifyAll实现
2.Lock + condition实现
- 多线程循环顺序打印:0102030405,线程0打印0,线程1打印奇数,线程2打印偶数
1.synchronized + wait + notifyAll实现
2.Lock + condition实现
12、基于ReentrantLock详解AQS
https://blog.csdn.net/Linging_24/article/details/134961383
13、CompleteFuture详解
https://blog.csdn.net/Linging_24/article/details/144832725?spm=1001.2014.3001.5501
14、ThraedLocal
https://blog.csdn.net/Linging_24/article/details/109708451?spm=1001.2014.3001.5501
更多推荐



所有评论(0)