个人网站: https://chenmingyu.top/concurrent-thread/
进程与线程
进程: 操作系统在运行一个程序的时候就会为其创建一个进程 (比如一个 java 程序), 进程是资源分配的最小单位, 一个进程包含多个线程
线程: 线程是 CPU 调度的最小单位, 每个线程拥有各自的计数器, 对战和局部变量等属性, 并且能过访问共享的内存变量
线程的状态
java 线程的生命周期总共包括 6 个阶段:
初始状态: 线程被创建, 但是还没有调用 start() 方法
运行状态: java 中将就绪状态和运行状态统称为运行状态
阻塞状态: 线程阻塞, 线程等待进入 synchronized 修饰的代码块或方法
等待状态: 线程进入等待状态, 需要调用 notify() 或 notifyAll() 进行唤醒
超时等待状态: 线程进入等待状态, 在指定时间后自行返回
终止状态: 线程执行完毕
在某一时刻, 线程只能处于其中的一个状态
线程初始化后, 调用 start() 方法变为运行状态, 调用 wait(),join() 等方法, 线程由运行状态变为等待状态, 调用 notify() 或 notifyAll() 等方法, 线程由等待状态变成运行状态, 超时等待状态就是在等待状态基础上加了时间限制, 超过规定时间, 自动更改为运行状态, 当需要执行同步方法时, 如果没有获得锁, 这时线程状态就变为阻塞状态, 直到获取到锁, 变为运行状态, 当执行完线程的 run() 方法后, 线程变为终止状态
创建线程
创建线程有三种方式
继承 Thread 类
实现 Runnable 接口
实现 Callable 接口
继承 Thread 类
- /**
- * @author: chenmingyu
- * @date: 2019/4/8 15:13
- * @description: 继承 Thread 类
- */
- public class ThreadTest extends Thread{
- @Override
- public void run() {
- IntStream.range(0,10).forEach(i->{
- System.out.println(this.getName()+":"+i);
- });
- }
- public static void main(String[] args) {
- Thread thread = new ThreadTest();
- thread.start();
- }
- }
实现 Runnable 接口
- /**
- * @author: chenmingyu
- * @date: 2019/4/8 15:18
- * @description: 实现 Runnable 接口
- */
- public class RunnableTest implements Runnable {
- @Override
- public void run() {
- IntStream.range(0,10).forEach(i->{
- System.out.println(Thread.currentThread().getName()+":"+i);
- });
- }
- public static void main(String[] args) {
- Runnable runnable = new RunnableTest();
- new Thread(runnable,"RunnableTest").start();
- }
- }
实现 Callable 接口
- /**
- * @author: chenmingyu
- * @date: 2019/4/8 15:23
- * @description: 实现 Callable 接口
- */
- public class CallableTest implements Callable<Integer> {
- @Override
- public Integer call() throws Exception {
- IntStream.range(0,10).forEach(i->{
- System.out.println(Thread.currentThread().getName()+":"+i);
- });
- return -1;
- }
- public static void main(String[] args) throws Exception {
- Callable callable = new CallableTest();
- FutureTask futureTask = new FutureTask(callable);
- new Thread(futureTask,"future").start();
- System.out.println("result:"+futureTask.get());
- }
- }
线程的暂停, 恢复, 停止
不安全的线程暂停, 恢复, 停止操作
Thread 提供的过期方法可以实现对线程进行暂停 suspend(), 恢复 resume(), 停止 stop() 的操作
例: 创建一个线程, run() 中循环输出当前时间, 在 main() 方法中对新建线程进行暂停, 恢复, 停止的操作
- /**
- * @author: chenmingyu
- * @date: 2019/4/8 15:51
- * @description: 线程的暂停, 恢复, 停止
- */
- public class OperationThread implements Runnable{
- @Override
- public void run() {
- while (true){
- try {
- TimeUnit.SECONDS.sleep(1L);
- System.out.println(Thread.currentThread().getName()+"运行中:"+LocalTime.now());
- }catch (InterruptedException e){
- System.err.println(e.getMessage());
- }
- }
- }
- public static void main(String[] args) throws Exception{
- Runnable runnable = new OperationThread();
- Thread thread = new Thread(runnable,"operationThread");
- /**
- * 启动, 输出当前时间
- */
- thread.start();
- TimeUnit.SECONDS.sleep(3L);
- /**
- * 线程暂停, 不在输出当前时间
- */
- System.out.println("此处暂停:"+LocalTime.now());
- thread.suspend();
- TimeUnit.SECONDS.sleep(3L);
- /**
- * 线程恢复, 继续输出当前时间
- */
- System.out.println("此处恢复:"+LocalTime.now());
- thread.resume();
- TimeUnit.SECONDS.sleep(3L);
- /**
- * 线程停止, 不在输出当前时间
- */
- thread.stop();
- System.out.println("此处停止:"+LocalTime.now());
- TimeUnit.SECONDS.sleep(3L);
- }
- }
输出
因为是过期方法, 所以不推荐使用, 使用 suspend() 方法后, 线程不会释放已经占有的资源, 就进入睡眠状态, 容易引发死锁问题, 而使用 stop() 方法终结一个线程是不会保证线程的资源正常释放的, 可能会导致程序异常
安全的线程暂停, 恢复, 停止操作
线程安全的暂停, 恢复操作可以使用等待 / 通知机制代替, 安全的停止操作可以用线程是否被中断进行判断
安全的线程暂停, 恢复 (等待 / 通知机制)
相关方法:
方法名 | 描述 |
---|---|
notify() | 通知一个在对象上等待的线程,使其重 wait() 方法中返回,前提是该线程获得了对象的锁 |
notifyAll() | 通知所有等待在该对象上的线程 |
wait() | 调用该方法线程进入等待状态,只有等待另外线程的通知或被中断才会返回,调用该方法会释放对象的锁 |
wait(long) | 超时等待一段时间(毫秒),如果超过时间就返回 |
wait(long,int) | 对于超时时间耕细粒度的控制,可以达到纳秒 |
例: 创建一个名为 waitThread 的线程, 在 run() 方法, 使用中使用 synchronized 进行加锁, 以变量 flag 为条件进行 while 循环, 在循环中调用 LOCK.wait() 方法, 此时会释放对象锁, 由 main() 方法获得锁, 调用 LOCK.notify() 方法通知 LOCK 对象上等待的 waitThread 线程, 将其置为阻塞状态, 并将变量 flag 置为 true, 当 waitThread 线程再次获取对象锁之后继续执行余下代码
- /**
- * @author: chenmingyu
- * @date: 2019/4/8 20:00
- * @description: wait/notify
- */
- public class WaitNotifyTest {
- private static Object LOCK = new Object();
- private static Boolean FLAG = Boolean.TRUE;
- public static void main(String[] args) throws InterruptedException{
- Runnable r = new WaitThread();
- new Thread(r,"waitThread").start();
- TimeUnit.SECONDS.sleep(1L);
- synchronized (LOCK){
- System.out.println(Thread.currentThread().getName()+"唤醒 waitThread 线程:"+LocalTime.now());
- /**
- * 线程状态由等待状态变为阻塞状态
- */
- LOCK.notify();
- /**
- * 只有当前线程释放对象锁, waitThread 获取到 LOCK 对象的锁之后才会从 wait() 方法中返回
- */
- TimeUnit.SECONDS.sleep(2L);
- FLAG = Boolean.FALSE;
- }
- }
- public static class WaitThread implements Runnable {
- @Override
- public void run() {
- /**
- * 加锁
- */
- synchronized (LOCK){
- while (FLAG){
- try {
- System.out.println(Thread.currentThread().getName()+"运行中:"+LocalTime.now());
- /**
- * 线程状态变为等待状态
- */
- LOCK.wait();
- /**
- * 再次获得对象锁之后, 才会执行
- */
- System.out.println(Thread.currentThread().getName()+"被唤醒:"+LocalTime.now());
- }catch (InterruptedException e){
- System.err.println(e.getMessage());
- }
- }
- }
- System.out.println(Thread.currentThread().getName()+"即将停止:"+LocalTime.now());
- }
- }
- }
输出
可以看到在 mian 线程调用 LOCK.notify() 方法后, 沉睡了 2s 才释放对象锁, waitThread 线程在获得对象锁之后执行余下代码
安全的线程停止操作 (中断标识)
线程的安全停止操作是利用线程的中断标识来实现, 线程的中断属性表示一个运行汇总的线程是否被其他线程进行了中断操作, 其他线程通过调用该线程的 interrupt() 方法对其进行中断操作, 而该线程通过检查自身是否被中断来进行响应, 当一个线程被中断可以使用 Thread.interrupted() 方法对当前线程的中断标识位进行复位
例: 新建一个线程, run 方法中使用 Thread.currentThread().isInterrupted() 是否中断作为判断条件, 在主线程中使用 thread.interrupt() 方法对子线程进行中断操作, 用来达到终止线程的操作, 这种方式会让子线程可以去清理资源或一些别的操作, 而使用 stop() 方法则会会直接终止线程
- /**
- * @author: chenmingyu
- * @date: 2019/4/8 20:47
- * @description: 中断
- */
- public class InterruptTest {
- public static void main(String[] args) throws InterruptedException {
- Runnable r = new StopThread();
- Thread thread = new Thread(r,"stopThread");
- thread.start();
- TimeUnit.SECONDS.sleep(1L);
- System.out.println(Thread.currentThread().getName()+"对 stopThread 线程进行中断:"+LocalTime.now());
- thread.interrupt();
- }
- public static class StopThread implements Runnable {
- @Override
- public void run() {
- while (!Thread.currentThread().isInterrupted()){
- System.out.println(Thread.currentThread().getName()+"运行中:"+LocalTime.now());
- }
- System.out.println(Thread.currentThread().getName()+"停止:"+LocalTime.now());
- }
- }
- }
未完待续...
参考: java 并发编程的艺术
来源: https://www.cnblogs.com/cmyxn/p/10673601.html