2929>
3030> [ 大彬的学习圈] ( https://topjavaer.cn/zsxq/introduce.html ) ** 加入方式** :
3131>
32- > ![ ] ( http://img.topjavaer.cn/img/星球优惠券 .png )
32+ > ![ 前面内容 ] ( http://img.topjavaer.cn/img/202304212238005 .png )
3333
3434## 线程池
3535
@@ -214,7 +214,33 @@ public static ExecutorService newCachedThreadPool() {
214214
215215适用场景:周期性执行任务的场景,需要限制线程数量的场景。
216216
217+ ### 怎么判断线程池的任务是不是执行完了?
217218
219+ 有几种方法:
220+
221+ 1、使用线程池的原生函数** isTerminated()** ;
222+
223+ executor提供一个原生函数isTerminated()来判断线程池中的任务是否全部完成。如果全部完成返回true,否则返回false。
224+
225+ 2、** 使用重入锁,维持一个公共计数** 。
226+
227+ 所有的普通任务维持一个计数器,当任务完成时计数器加一(这里要加锁),当计数器的值等于任务数时,这时所有的任务已经执行完毕了。
228+
229+ 3、** 使用CountDownLatch** 。
230+
231+ 它的原理跟第二种方法类似,给CountDownLatch一个计数值,任务执行完毕后,调用countDown()执行计数值减一。最后执行的任务在调用方法的开始调用await()方法,这样整个任务会阻塞,直到这个计数值为零,才会继续执行。
232+
233+ 这种方式的** 缺点** 就是需要提前知道任务的数量。
234+
235+ 4、** submit向线程池提交任务,使用Future判断任务执行状态** 。
236+
237+ 使用submit向线程池提交任务与execute提交不同,submit会有Future类型的返回值。通过future.isDone()方法可以知道任务是否执行完成。
238+
239+ ### 为什么要使用Executor线程池框架呢?
240+
241+ - 每次执行任务都通过new Thread()去创建线程,比较消耗性能,创建一个线程是比较耗时、耗资源的
242+ - 调用new Thread()创建的线程缺乏管理,可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪
243+ - 直接使用new Thread()启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不好实现
218244
219245## 进程线程
220246
@@ -528,7 +554,10 @@ interrupt() 并不能真正的中断线程,需要被调用的线程自己进
528554
529555使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,线程自动转为Runnable状态。
530556
557+ ### 如何停止一个正在运行的线程?
531558
559+ 1 . 使用共享变量的方式。共享变量可以被多个执行相同任务的线程用来作为是否停止的信号,通知停止线程的执行。
560+ 2 . 使用interrupt方法终止线程。当一个线程被阻塞,处于不可运行状态时,即使主程序中将该线程的共享变量设置为true,但该线程此时根本无法检查循环标志,当然也就无法立即中断。这时候可以使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态。
532561
533562## volatile底层原理
534563
@@ -1177,17 +1206,6 @@ public final void lazySet(int i, int newValue)//最终 将index=i 位置的元
11771206- AtomicStampedReference:带有版本号的引用类型原子类。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
11781207- AtomicMarkableReference :原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来
11791208
1180- ## 为什么要使用Executor线程池框架呢?
1181-
1182- - 每次执行任务都通过new Thread()去创建线程,比较消耗性能,创建一个线程是比较耗时、耗资源的
1183- - 调用new Thread()创建的线程缺乏管理,可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪
1184- - 直接使用new Thread()启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不好实现
1185-
1186- ## 如何停止一个正在运行的线程?
1187-
1188- 1 . 使用共享变量的方式。共享变量可以被多个执行相同任务的线程用来作为是否停止的信号,通知停止线程的执行。
1189- 2 . 使用interrupt方法终止线程。当一个线程被阻塞,处于不可运行状态时,即使主程序中将该线程的共享变量设置为true,但该线程此时根本无法检查循环标志,当然也就无法立即中断。这时候可以使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态。
1190-
11911209## 什么是Daemon线程?
11921210
11931211后台(daemon)线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这个线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会终止。必须在线程启动之前调用setDaemon()方法,才能把它设置为后台线程。
@@ -1203,28 +1221,6 @@ SynchronizedMap一次锁住整张表来保证线程安全,所以每次只能
12031221JDK1.8 ConcurrentHashMap采用CAS和synchronized来保证并发安全。数据结构采用数组+链表/红黑二叉树。synchronized只锁定当前链表或红黑二叉树的首节点,支持并发访问、修改。
12041222另外ConcurrentHashMap使用了一种不同的迭代方式。当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据 ,iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。
12051223
1206- ## 怎么判断线程池的任务是不是执行完了?
1207-
1208- 有几种方法:
1209-
1210- 1、使用线程池的原生函数** isTerminated()** ;
1211-
1212- executor提供一个原生函数isTerminated()来判断线程池中的任务是否全部完成。如果全部完成返回true,否则返回false。
1213-
1214- 2、** 使用重入锁,维持一个公共计数** 。
1215-
1216- 所有的普通任务维持一个计数器,当任务完成时计数器加一(这里要加锁),当计数器的值等于任务数时,这时所有的任务已经执行完毕了。
1217-
1218- 3、** 使用CountDownLatch** 。
1219-
1220- 它的原理跟第二种方法类似,给CountDownLatch一个计数值,任务执行完毕后,调用countDown()执行计数值减一。最后执行的任务在调用方法的开始调用await()方法,这样整个任务会阻塞,直到这个计数值为零,才会继续执行。
1221-
1222- 这种方式的** 缺点** 就是需要提前知道任务的数量。
1223-
1224- 4、** submit向线程池提交任务,使用Future判断任务执行状态** 。
1225-
1226- 使用submit向线程池提交任务与execute提交不同,submit会有Future类型的返回值。通过future.isDone()方法可以知道任务是否执行完成。
1227-
12281224## 什么是Future?
12291225
12301226在并发编程中,不管是继承thread类还是实现runnable接口,都无法保证获取到之前的执行结果。通过实现Callback接口,并用Future可以来接收多线程的执行结果。
0 commit comments