Java 多线程-基础知识梳理(一)

Java 多线程-基础知识梳理(一)

2020-04-14    08'54''

主播: 橙汁儿橙汁儿。

202 3

介绍:
关于进程与线程的理解 启动线程的 3 种方式 6个线程的状态 Object类的 wait() 方法 Object类的 notify() 方法 Thread类的 sleep() 方法 线程让步 yield() 方法 等待线程执行完毕的 join() 方法 会抛InterruptedException异常的方法 关于中断的几个方法 守护线程 死锁 局部线程变量ThreadLocal < T > 虚假唤醒 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 < T > 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 时间片,由一个线程切换到另一个线程的过程就叫 “线程上下文切换”。
上一期:
下一期: Java 多线程编程-基础知识(二)