@@ -356,7 +356,9 @@ Future也是一个接口,Future就像是一个管理的容器一样,进一
356356
357357``` java
358358public interface Future <V> {
359-
359+ // mayInterruptIfRunning代表是否强制中断
360+ // 为true,如果任务已经执行,那么会调用Thread.interrupt()方法设置中断标识
361+ // 为false,如果任务已经执行,就只会将任务状态标记为取消,而不会去设置中断标识
360362 boolean cancel (boolean mayInterruptIfRunning );
361363
362364 boolean isCancelled ();
@@ -642,7 +644,7 @@ public enum State {
642644
643645#### BLOCKED 阻塞态
644646
645- 阻塞状态。处于BLOCKED状态的线程正等待锁的释放以进入同步区 。
647+ 阻塞状态。线程没有申请到synchronize同步锁,就会处于阻塞状态,等待锁的释放以进入同步区 。
646648
647649#### WAITING 等待态
648650
@@ -825,11 +827,158 @@ thread.interrupted()
825827
826828### 线程执行的任务可以终止吗?
827829
830+ ##### 1.设置中断
831+
828832FutureTask提供了cancel(boolean mayInterruptIfRunning)方法来取消任务,并且
829833
830834如果入参为false,如果任务已经在执行,那么任务就不会被取消。
831835
832- 如果入参为true,如果任务已经在执行,那么会调用Thread的interrupt()方法来设置线程的中断标识,如果线程处于阻塞状态,会抛出异常,如果正常状态只是设置标志位,修改interrupted变量的值。所以如果要取消任务只能在任务内部中调用thread.isInterrupted()方法获取当前线程的中断状态,自行取消。
836+ 如果入参为true,如果任务已经在执行,那么会调用Thread的interrupt()方法来设置线程的中断标识,如果线程处于阻塞状态,会抛出InterruptedException异常,如果正常状态只是设置标志位,修改interrupted变量的值。所以如果要取消任务只能在任务内部中调用thread.isInterrupted()方法获取当前线程的中断状态,自行取消。
837+
838+ ##### 2.线程的stop方法
839+
840+ 线程的stop()方法可以让线程停止执行,但是不会释放当前线程持有的锁。但是这个方法已经被标记为过期,不推荐使用了。
841+
842+ ### 让线程顺序执行有哪些方法?
843+
844+ ##### 1.主线程Join
845+
846+ 就是调用threadA.start()方法让线程A先执行,然后主线程调用threadA.join()方法,然后主线程进入TIME_WAITING状态,直到threadA执行结束后,主线程才能继续往下执行,执行线程B的任务。(join方法的底层实现其实是调用了threadA的wait()方法,当线程A执行完毕后,会自动调用notifyAll()方法唤醒所有线程。)
847+
848+ 示例代码如下:
849+
850+ ``` java
851+ Thread threadA = new Thread (new Runnable () {
852+ @Override
853+ public void run () {
854+ // 执行threadA的任务
855+ }
856+ });
857+ Thread threadB= new Thread (new Runnable () {
858+ @Override
859+ public void run () {
860+ // 执行threadB的任务
861+ }
862+ });
863+ // 执行线程A任务
864+ threadA. start();
865+ // 主线程进行等待
866+ threadA. join();
867+ // 执行线程B的任务
868+ threadB. start();
869+ ```
870+
871+ ##### 子线程Join
872+
873+ 就是让线程B的任务在执行时,调用threadA.join()方法,这样就只有等线程A的任务执行完成后,才会执行线程B。
874+
875+ ``` java
876+ Thread threadA = new Thread (new Runnable () {
877+ @Override
878+ public void run () {
879+ // 执行threadA的任务
880+ }
881+ });
882+ Thread threadB= new Thread (new Runnable () {
883+ @Override
884+ public void run () {
885+ // 子线程进行等待,知道threadA任务执行完毕
886+ threadA. join();
887+ // 执行threadB的任务
888+ }
889+ });
890+ // 执行线程A任务
891+ threadA. start();
892+ // 执行线程B的任务
893+ threadB. start();
894+ ```
895+ ##### 单线程池法
896+
897+ 就是使用Executors.newSingleThreadExecutor()这个线程池,这个线程池的特点就是只有一个执行线程,可以保证任务按顺序执行。
898+
899+ ``` java
900+ ExecutorService pool = Executors . newSingleThreadExecutor();
901+ // 提交任务A
902+ executorService. submit(taskA);
903+ // 提交任务B
904+ executorService. submit(taskB);
905+ ```
906+
907+ ##### 等待通知法(wait和notify)
908+
909+ 就是在线程B中调用Object.waiting()方法进行等待,线程A执行完毕后调用Object.notify()方法进行唤醒。(这种方法有两个缺点,一个是Object.waiting()和notify()方法必须在同步代码块中调用,第二个是如果线程A执行过快,先调用了object.notify()方法,就会导致线程B后面一直得不到唤醒。)
910+
911+ ``` java
912+ final Object object = new Object ();
913+ Thread threadA = new Thread (new Runnable () {
914+ @Override
915+ public void run () {
916+ // 执行threadA的任务
917+ synchronized (object) {
918+ object. notify();
919+ }
920+ }
921+ });
922+ Thread threadB= new Thread (new Runnable () {
923+ @Override
924+ public void run () {
925+ synchronized (object) {
926+ // 子线程进行等待,知道threadA任务执行完毕
927+ object. wait();
928+ // 执行threadB的任务
929+ }
930+ }
931+ });
932+ ```
933+
934+ ##### 等待通知法(await和singal)
935+
936+ 具体实现就是Reentrantlock可以创建出一个Condition实例queue,可以认为是一个等待队列,线程B调用queue.await()就会进行等待,直到线程A执行完毕调用queue.signal()来唤醒线程B。
937+
938+ ``` java
939+ final ReentrantLock lock = new ReentrantLock ();
940+ final Condition queue1 = lock. newCondition();
941+ final Object object = new Object ();
942+ final Thread threadA = new Thread (new Runnable () {
943+ @Override
944+ public void run () {
945+ // 执行threadA的任务
946+ lock. lock();
947+ try {
948+ // 唤醒线程B的任务
949+ queue1. signal();
950+ } catch (InterruptedException e) {
951+ e. printStackTrace();
952+ }
953+ System . out. println(" 执行了任务A2" );
954+ lock. unlock();
955+ }
956+ });
957+ final Thread threadB= new Thread (new Runnable () {
958+ @Override
959+ public void run () {
960+ lock. lock();
961+ // 子线程进行等待,知道threadA任务执行完毕
962+ try {
963+ queue1. await();
964+ System . out. println(" 执行了任务B2" );
965+
966+ } catch (InterruptedException e) {
967+ e. printStackTrace();
968+ }
969+ // 执行threadB的任务
970+ lock. unlock();
971+ }
972+ });
973+ threadA. start();
974+ threadB. start();
975+ ```
976+
977+
978+
979+ 参考链接:
980+
981+ http://cnblogs.com/wenjunwei/p/10573289.html
833982
834983### 线程间怎么通信?
835984
@@ -986,8 +1135,6 @@ Integer consumer() {
9861135 return value;
9871136 }
9881137}
989-
990-
9911138```
9921139
9931140完整代码如下:
@@ -1436,10 +1583,11 @@ public ScheduledThreadPoolExecutor(int corePoolSize,
14361583最佳线程数= CPU 的数量+ 1
14371584```
14381585#### 方法二 IO 密集型任务
1439- 这种任务在执行时,需要进行一些IO 操作,所以为了充分利用CPU ,应该在线程进行IO 操作时,就让出时间片,CPU 进行上下文切换,执行其他线程的任务,保证 CPU 利用率的 100 % 。
1586+ 这种任务在执行时,需要进行一些IO 操作,所以为了充分利用CPU ,应该在线程进行IO 操作时,就让出时间片,CPU 进行上下文切换,执行其他线程的任务,保证 CPU 利用率尽可能达到 100 % 。
14401587
1441- 如果任务有50 % 的时间需要CPU 执行状态,其他时间进行IO 操作,则程序所需线程数为CPU 数量的1 除以0.5 ,也就是2 倍。如果任务有20 % 的时时间需要CPU 执行,其他时间需要进行IO 操作,最佳线程数也就是1 除以0.2 ,也就是CPU 数的5 倍
1588+ 如果任务有50 % 的时间需要CPU 执行状态,其他时间进行IO 操作,则程序所需线程数为CPU 数量的1 除以0.5 ,也就是2 倍。如果任务有20 % 的时时间需要CPU 执行,其他时间需要进行IO 操作,最佳线程数也就是1 除以0.2 ,也就是CPU 数的5 倍。
14421589所以公式为
1590+
14431591```java
14441592最佳线程数 = CPU 数量/ (每个任务中需要CPU 执行的时间的比例)
14451593= CPU 数量/ (CPU 运行时间/ 任务执行总时间)= CPU 数量/ (CPU 运行时间/ (CPU 运行时间+ IO 操作时间))
@@ -1455,6 +1603,7 @@ public ScheduledThreadPoolExecutor(int corePoolSize,
14551603一种是追求响应时间的任务,例如使用线程池对发起多个网络请求,然后对结果进行计算。 这种任务的最大线程数需要设置大一点,然后队列使用同步队列,队列中不缓存任务,任务来了就会被执行。判断线程池资源不够用时,一般是发现活跃线程数/ 最大线程数> 阀值(默认是0.8 )时,或者是线程池抛出的RejectedExecut 异常次数达到阀值,就会进行告警。然后程序员收到告警后,动态发送修改核心线程数,最大线程数,队列相关的指令,服务器进行动态修改。
14561604
14571605##### 追求高吞吐量的任务
1606+
14581607假设说需要定期自动化生成一些报表,不需要考虑响应时间,只是希望如何使用有限的资源,尽可能在单位时间内处理更多的任务,也就是吞吐量优先的问题。
14591608这种就是使用有界队列,对任务进行缓存,然后线程进行并发执行。判断线程池资源不够用时,一般是发现等待队列中的任务数量/ 等待队列的长度> 阀值(默认是0.8 )时,或者是线程池抛出的RejectedExecut 异常次数达到阀值,就会进行告警。然后程序员收到告警后,动态发送修改核心线程数,最大线程数,队列相关的指令,服务器进行动态修改。
14601609
@@ -1544,7 +1693,7 @@ ThreadLocal变量所在的类的实例(代码中A的实例)->ThreadLocal
15441693
15451694可以看到ThreadLocal 变量不仅被所在的类A 的实例所引用,还被执行的线程所引用,
15461695
1547- 1. 如果使用强引用,也就是线程对ThreadLocal 变量是强引用,那么类 A 的实例即便已经不被其他变量所引用了,不会被访问到了,需要被回收了,类 A 被回收了,但是 ThreadLocal 变量由于有被执行线程所引用,只要线程还在, ThreadLocal 变量也不能被被 JVM 回收,所以会造成内存泄露 。
1696+ 1. 如果使用强引用,也就是线程对ThreadLocal 变量是强引用,那么即便实例 A 被回收了,只要线程还没有被回收,线程的 ThreadLocalMap 还会引用这个key(也就是这个 ThreadLocal 遍历),导致这个key 没有被回收,造成内存泄露 。
15481697
154916982. 如果使用弱引用,不会影响key的回收,也就是不会影响引用了ThreadLocal 的实例对象的回收。
15501699
0 commit comments