java多线程-关键字
线程的生命周期
线程的生命周期(来源于书籍java高并发编程详解)NEW:
- 线程被创建的状态
RUNNABLE:
- 新建线程调用start方法后
- yield方法调用后,主动结束running状态
- sleep结束后
- wait之后,调用notify/notifyAll后
- running状态也为runnable状态,直接可以再次running
- CPU放弃running状态的线程
RUNNING:
- runnable状态的线程,被CPU选中
BLOCKED:
- sleep方法
- wait方法
- IO阻塞
TERMINATED:
- 线程正常结束
- stop(已经废弃)
- 意外死亡
关键字
sleep:
- sleep是一个静态方法也是线程(Thread)的方法,需要设置休眠时间,调用后进入阻塞(BLOCKED)状态,休眠时间结束后进入可运行状态(RUNNABLE);
- 可以被中断,中断后需要捕获中断异常,捕获异常后会擦除中断标记。
- sleep在阻塞情况下不会放弃锁资源。
示例(当前线程休眠一分钟):
Thread.sleep(60*1000L);
或者
TimeUnit.MINUTES.sleep(1);
yield:
- 该方法是一种启发式方法,提醒调度器自愿放弃当前CPU资源,如果CPU的资源不紧张,则会忽略。
- 该方法会使得当前线程从运行(RUNNING)状态转换到可运行(RUNNABLE)状态,一般该方法不常用。
- 该方法不会被中断
join:
- 可被中断。
- 在A线程中,调用B线程的join()方法,会使得A线程进入阻塞状态,等待B线程结束后执行。
interrupt:
该方法使得阻塞状态的线程可以中断阻塞。中断后只是给当前线程标记一种中断的状态,并不是使线程直接停止。对于因为调用wait()、sleep()、join()方法而导致的阻塞会通过捕获异常来处理该状态,异常捕获后会擦除中断标记。
isInterrupted:
- 该方法是Thread的成员方法。
- 主要做判断,该线程是否被中断。
- 含有返回值,表示是否中断。
interrupted:
- 该方法是一个静态方法
- 除了判断该线程是否中断外,会擦除中断标记。
volatile:
并发编程的三个重要特征:原子性、可见性、有序性
原子性:是指在一次或多次操作中,要么所有的操作全部都执行并不会受任何因素的干扰而中断,要么所有的操作都不执行。
可见性:当一个线程对共享变量修改后,其他线程可立即看到最新的值。
有序性:所谓有序性是指程序代码在执行过程中的先后顺序,由于java编译和运行期的优化,会使得代码的执行顺序未必是开发者编写代码的顺序。
- JMM(java内存模型)中,每个线程有自己的工作内存(CPU 寄存器)并且会使用多级缓存来提高CPU的吞吐量,线程共享资源存放在主存(RAM)中。每个线程在操作数据时,会先从缓存获取数据,如果没有获取到再去主存中获取并添加到缓存中。高并发时,多线程修改共享资源,由于缓存机制,会使得主存中共享资源出现错误数据。
- 使用volatile修饰的变量,线程在读取该共享资源的数据时,也会使用缓存机制,但是在修改该资源时,会将该线程缓存中的资源一并修改,并刷新到主存中 。然后使得其他线程缓存中的数据失效。
JMM与CPU硬件架构交互图- 使用volatile修饰的变量,保证了不同线程之间对共享变量操作时的可见性,也就是说当一个线程修改后,另一个线程会立即看到最新的值(可见性)。
- 禁止对指令进行重新排序操作(有序性);
- 不能保证原子性
synchronized:
- 可修饰方法或代码块
- 一种互斥机制,同一时刻只能允许一个线程访问该修饰符修饰的资源
- 可保证可见性(锁退出时所有共享资源会刷新到主存中)、原子性(同步代码块无法被中途打断)与顺序性(因为同步使得程序串行化所以保证了顺序性)
- 锁对象不能为空
缺点:无法控制时长,也不能被中断(例如:AB线程使用同一锁对象,A持有锁并且需要很长时间才能释放,B等待锁释放的最长时间小于A释放的时间,)
wait:
- 是该线程进入阻塞状态
- 该方法为Object方法
- 必须拥有该对象的锁,也就是必须在同步方法中使用
- 当前线程使用后,会放弃锁资源。并进入该锁的wait set中。
- 该方法可被中断。
notify/notifyAll:
- 该方法可唤醒,同锁且在锁对象wait set中的线程。
- notify唤醒该锁 wait set 中的一个。
- notifyAll唤醒 wait set 中的所有线程。