关于进程与线程的理解
启动线程的 3 种方式
6个线程的状态
Object类的 wait() 方法
Object类的 notify() 方法
Thread类的 sleep() 方法
线程让步 yield() 方法
等待线程执行完毕的 join() 方法
会抛InterruptedException异常的方法
关于中断的几个方法
守护线程
死锁
局部线程变量ThreadLocal
虚假唤醒
sleep 方法与 yield 方法的区别
理解线程上下文切换
------------------------------------------------------------------------------
1.关于进程与线程的理解:
进程是操作系统进行资源的分配和调度的单位,而 CPU 资源比较特殊,线程是分配和调度 CPU资源 的单位。
每一个程序相当于一个进程,程序中的任务相当于一个线程。
进程是有自己的一套变量,而线程是共享进程的变量。
进程之间是相互独立的,一个进程终止不会影响到其他进程,而线程是相互依赖的,在 Linux 中,父线程终止,所有子线程也会终止。而一个子线程终止,不会影响到其他子线程。除非子线程调用 exit() 方法,其他子线程也会终止。
而在 Java 中,父线程的生命周期与子线程无关,父线程终止,子线程也可以继续执行。
进程 ID 是 int 类型,而 线程 ID 是 long 类型,范围比较广。
2.启动线程的 3 种方式
Thread 类
实现 Runnable 接口 需要覆写 run() 方法
异步:FutureTask 需要覆写 call()方法
new Thread(futuretask)
启动线程都是用 start()方法。
3.6个线程的状态:
使用 new 关键字创建对象后: NEW
使用 .start() RUNNABLE 就绪状态
使用 wait()/wait(long timeout) WAIT 等待/超时等待
被阻塞 BLOCKING
遇到异常或 run() 方法正常执行完毕 TERMINAL 终止
4.Object类的 wait() 方法:
必须在同步块中使用,也就是获取到 对象的监视器锁,否则会抛出 IllegalMonitorStateException 异常;
如果传入超时参数,即使没有被其他线程用 notify()/notifyAll() 唤醒,还是会因为超时而返回。
5.Object类的 notify() 方法:
必须在同步块中使用,使因为调用 wait() 方法而被阻塞的线程变为就绪状态,可以参加 CPU 的竞争。
notify() 只唤醒一个,而 notifyAll() 唤醒所有。
6.Thread类的 sleep() 方法:
静态方法,使当前线程睡眠,交出 CPU 时间片,线程挂起,但不会释放锁。
如果传入的时间是负数,会抛 IllegalParamaterException。
如果传入 0 ,意味着对当前所有的线程进行总优先级重新排序。
经过睡眠时间后,线程就会处于就绪状态,与其他线程竞争 CPU 。
7.线程让步 yield() 方法:
静态方法,表示 暗示线程调度器 当前线程 currentThread 愿意交出 CPU 时间片,然后这个线程会进入就绪状态,线程调度器可以无条件忽视这个请求。
-------------------------------------------------------------------------
sleep() 方法 和 yield() 方法的区别:
sleep()方法是使线程进入阻塞挂起状态,而 yield() 是使线程进入就绪状态。
-------------------------------------------------------------------------
8.等待线程执行完毕的 join() 方法:
比如说 在主线程中调用 线程A 的 join() ,主线程会阻塞,直到线程 A 执行完毕,主线程才会继续向下进行。
9.会抛InterruptedException异常的方法:
wait()、join()、sleep()。
10.关于中断的几个方法:
并不是真正地使程序中断,只是将中断标志位设置为 true。
interrupt() :将标志位设置为 ture。
isinterrupt() :返回标志位值。
isinterrupted() : Thread类的静态方法,会先获取当前线程 currentThread,如果当前线程中断位是 true,就会返回 true并清除中断标志位,即修改为 false,否则返回 false。
11.守护线程
为用户线程提供相应操作的,比如 GC 垃圾回收就是守护线程。
所有用户线程执行完毕,(不论守护线程是否执行完毕)JVM 就会退出。
12.死锁 需要满足 4 个条件:
(1)互斥条件:线程对资源的占用是排他的。资源只能被一个线程占有。
(2)线程要持有一个资源并请求其他资源:
比如线程 A 持有资源 A,并请求资源 B。
(3)不可剥夺条件:线程使用资源时,除非自己交出 CPU,其他线程是不可剥夺资源的。
(4)环形等待回路。
想解除死锁只要破坏其中一个条件即可,比如:按序获取资源。
13.本地线程变量ThreadLocal
ThreadLocal 是个工具类,在 Thread 中有个 ThreadLoaclMap 类型的 threadLocals 变量,是懒加载的,需要用的时候才会创建。可以使每个 thread 对象绑定一个本地变量。ThreadLoaclMap是 ThreadLocal 的一个内部类,这个 map 的 key 值是 ThreadLocal 类型的,value 值是 绑定的变量 Object 类型的,而且底层是 Entry 类型的数组,Enry 只继承了 WeakReference 弱引用 。当调用 ThreadLocal 的 set 方法时,会先获取当前线程 CurrentThread,通过线程对象 getMap,如果 map 为 null 的话,就会用 map 的 set 传入 threadLocal 和 value,否则会将 线程对象的 threalLocals 设置为 更新后的 value 对应的 threadLocalMap。
因为是弱引用类型,所以如果当前线程不消亡,本地线程变量就会一直存在,所以需要用 remove ,避免内存溢出。
使用本地变量的好处就是每个线程对象会有自己专属的变量,不需要同步处理。与此类似的还有 ThreadLocalRandomSeed。
14.虚假唤醒
因调用 wait()方法阻塞的线程,未经过 notify()/notifyAll() 也可能被唤醒,所以应该用 while(条件不满足)
{ wait();}
15.理解线程上下文切换
一个线程使用完 CPU 或 主动交出 CPU 时间片,就会处于就绪状态,而其他线程竞争到了 CPU 时间片,由一个线程切换到另一个线程的过程就叫 “线程上下文切换”。