1.创建线程的3种方式
继承Thread类
实现Runnable接口
实现Callable接口
2.线程的5种状态
初始、就绪、运行、阻塞、死亡
3.线程的相关API
sleep会让当前线程从 Running 进入 Timed Waiting 状态(阻塞),不会释放对象锁
yield会释放CPU资源,让当前线程从 Running 进入 Runnable就绪状态,让优先级更高(至少是相同)的线程获得执行机会,不会释放对象锁
join,方法用于让当前线程等待另一个线程执行完毕。一般用于等待异步线程执行完结果之后才能继续运行的场景
wait会释放锁。因为wait前先获取到锁,通过notify唤醒时候,notify的线程也要加锁,如果wait不释放锁,notify就拿不到锁,也无法唤醒wait的线程
notify():通知一个在对象上等待的线程,使其从wait方法返回,而返回的前提是该线程获取到了对象的锁,没有获得锁的线程重新进入WAITING状态。
notifyAll():通知所有等待在该对象上的线程。尽可能用notifyAll(),谨慎使用notify(),因为notify()只会唤醒一个线程,我们无法确保被唤醒的这个线程一定就是我们需要唤醒的线程。
4.线程的优先级
线程优先级会提示调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它。如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用。
在Java线程中,通过一个整型成员变量priority来控制优先级,优先级的范围从1~10,在线程构建的时候可以通过setPriority(int)方法来修改优先级,默认优先级是5,优先级高的线程分配时间片的数量要多于优先级低的线程。
在不同的JVM以及操作系统上,线程规划会存在差异,有些操作系统甚至会忽略对线程优先级的设定。
5.守护线程
默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。
6.守护线程的应用场景
JVM中垃圾回收器就采用了守护线程,如果一个程序中没有任何用户线程,那么就不会产生垃圾,垃圾回收器也就不需要工作了。
7.线程的终止
不要使用stop方法。stop()方法已经被jdk废弃,调用stop方法无论run()中的逻辑是否执行完,都会释放CPU资源,释放锁资源,会释放当前线程所持有的所有的锁。这会导致线程不安全。
isInterrupted(),判断是否被中断,不会清除 中断标记
interrupted(),判断当前线程是否被中断 ,会清除中断标记
interrupt(),中断线程,如果被中断线程正在 sleep,wait,join 会导致被中断的线程抛出 InterruptedException,并清除 中断标记 ;如果中断的正在运行的线程,则会设置中断标记 ;park 的线程被中断,也会设置中断标记
8.中断机制
安全的中止则是其他线程通过调用某个线程A的interrupt()方法对其进行中断操作, 中断好比其他线程对该线程打了个招呼,“A,你要中断了”,不代表线程A会立即停止自己的工作,同样的A线程完全可以不理会这种中断请求。线程通过检查自身的中断标志位是否被置为true来进行响应。
注意:处于死锁状态的线程无法被中断。
9.volatile关键字
volatile保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
volatile最适用的场景:一个线程写,多个线程读。
根据JMM的规定,线程对共享变量的所有操作都必须在自己的本地内存中进行,不能直接从主内存中读取。
10.LockSupport
LockSupport是JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待“许可”,调用unpark则为指定线程提供“许可”。
如果这个许可还没有被占用,当前线程获取许可并继续执行;如果许可已经被占用,当前线程阻塞,等待获取许可。
Java锁和同步器框架的核心AQS,是通过调用LockSupport.park()和LockSupport.unpark()实现线程的阻塞和唤醒的。
park(),当没有『许可』时,当前线程暂停运行;有『许可』时,用掉这个『许可』,当前线程恢复运行。
LockSupport允许先调用unpark(Thread t),后调用park()。如果thread1先调用unpark(thread2) - 发放许可,然后线程2后调用park(),线程2是不会阻塞的。如果线程1先调用notify,然后线程2再调用wait的话,线程2是会被阻塞的。
LockSupport.park()和unpark()随时随地都可以调用。而wait和notify只能在synchronized代码段中调用。
11.LockSupport和synchronized区别
synchronized 关键字:
自动管理锁的获取和释放。
当异常发生时,锁会被自动释放,从而避免了死锁的风险。
在 Java 6 之后进行了许多优化,例如偏向锁、轻量级锁和重量级锁等,以提高性能。
LockSupport 类:
主要用于实现更复杂的同步结构,而不是直接用于同步代码块。
如果线程被中断,LockSupport.park() 方法会立即返回,但不会抛出 InterruptedException。
通常与其他同步工具(如 Condition)一起使用。
总结:
在大多数情况下,对于一般的同步需求,synchronized 和 ReentrantLock(或其他 Lock 实现)会是更合适的选择。LockSupport 更适合于那些需要构建自定义同步逻辑的场景。