Java线程的基本概念和五种状态-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

1. 线程

1.1 创建线程

创建线程通常有以下三种方式

实现 Runnable 接口并重写其 run 方法

public class J1_Method01 {
    public static void main(String[] args) {
        System.out.println("Main线程的ID为" + Thread.currentThread().getId());
        Thread thread = new Thread(new CustomRunner());
        thread.start();
    }
}

class CustomRunner implements Runnable {
    @Override
    public void run() {
        System.out.println("CustomRunner线程的ID为" + Thread.currentThread().getId());
    }
}

继承自 Thread 类并重写其 run 方法

public class J2_Method02 {
    public static void main(String[] args) {
        System.out.println("Main线程的ID为" + Thread.currentThread().getId());
        CustomThread customThread = new CustomThread();
        customThread.start();
    }
}

class CustomThread extends Thread {
    @Override
    public void run() {
        System.out.println("CustomThread线程的ID为" + Thread.currentThread().getId());
    }
}

以上两种方式都无法获取线程的返回值如果想要获取线程的返回值需要实现 Callable 接口

public class J3_Method03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Task task = new Task();
        FutureTask<Integer> futureTask = new FutureTask<>(task);
        new Thread(futureTask).start();
        System.out.println("获得线程返回值" + futureTask.get());
    }
}

class Task implements Callable<Integer> {
    @Override
    public Integer call() {
        return 100;
    }
}
// 输出
获得线程返回值100

1.2 线程属性

编号 (ID) 用于标识线程的唯一编号只读属性。

名称 (Name)用于定义线程名称可读可写。

线程类别 (Daemon)通过线程的 `setDaemon(boolean on)` 方法进行设置为 true 表示设置为守护线程否则为用户线程。用户线程会阻止 Java 虚拟机正常停止守护线程则不会。通常可以把一些不重要的线程设置为守护线程比如监控其他线程状态的监控线程当其他工作线程停止后虚拟机就可以正常退出。

优先级 (Priority)Java 线程支持 1 到 10 十个优先级默认值为 5 。Java 线程的优先级本质上只是给线程调度器一个提示信息它并不能保证线程一定按照优先级的高低顺序运行所以它是不可靠的需要谨慎使用。在 Java 平台中子线程的优先级默认与其父线程相同。

1.3 线程状态

Java 线程的生命周期分为以下五类状态

RUNABLE该状态包括两个子状态READY 和 RUNING 。处于 READY 状态的线程被称为活跃线程被线程调度器选中后才开始运行转化为 RUNING 状态。

BLOCKED一个线程发起一个阻塞式 IO 操作后如文件读写或者阻塞式 Socket 读写或者申请一个由其他线程持有的独占资源比如锁时相应的线程就会处于该状态。

WAITING线程处于无时间限制的等待状态。

TIMED_WAITING有时间限制的等待状态如果在指定时间内并没有执行的特定的操作则该线程自动转换为 RUNABLE。

TERMINATED`Thread.run()`正常返回或者由于抛出异常而提前终止则对应的线程都会处于该终止状态。

各个状态之间的转换关系如下图

1.4 线程终止

通常线程会随着代码的运行完成而终止但如果你在线程中进行了死循环操作此时就需要考虑如何安全地停止线程虽然 Thread 类提供了 `stop()` 方法但其已经被标识为废弃因为 `stop()` 只是暴力的停止线程 但此时线程中的操作仍可能处于中间状态此时暴力地停止就可能会产生非预期的结果。想要安全的停止线程可以通过改变终止标志位的方式来实现

public class ThreadStop {

    private static volatile boolean stopFlag = true;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (stopFlag) {
                try {
                    Thread.sleep(100);
                    System.out.println("持续输出");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        Thread.sleep(3 * 1000);
        stopFlag = false;
        System.out.println("线程终止");
    }
}

1.5  线程中断

除了终止线程外JDK 中提供了以下方法用于实现线程中断

Thread.interrupt() 用于给目标线程设置中断标志位但实际上并不能中断线程

Thread.isInterrupted()通过检查中断标志位判断当前线程是否被中断

Thread.interrupted()用来判断当前线程的中断状态同时会清除当前线程的中断标志位。

线程中断与线程终止的区别在于线程中断只是告诉目标线程我希望你停止运行即设置标志位而线程是否真的停止则是由其自行决定。示例如下

/** 
 * interrupt() 只是设置中断标志位并不能中断线程所以子线程会持续打印
 */
public class J1_Interrupt {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println("子线程打印");
            }
        });
        thread.start();
        Thread.sleep(10);
        thread.interrupt();
    }
}


/**
 * isInterrupted() 用于检查当前线程是否存在中断标志位配合interrupt()使用可以中断线程
 */
public class J2_IsInterrupted {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("子线程打印");
            }
        });
        thread.start();
        Thread.sleep(10);
        thread.interrupt();
    }
}

/**
 * interrupted() 用于判断当前线程的中断状态并清除当前线程的中断标志位
 */
public class J3_Interrupted {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            // 此时由于标志位被清除此时子线程依然会持续打印
            while (!Thread.interrupted() || !Thread.currentThread().isInterrupted()) {
                System.out.println("子线程打印");
            }
        });
        thread.start();
        Thread.sleep(10);
        thread.interrupt();
    }
}

2. 基本概念

2.1 变量分类

状态变量 (State Variable) 即类的实例变量非共享的静态变量。

共享变量 (Shared Variable) : 即可以被多个线程共同访问的变量。

2.2  竞态

如果多个线程并发的操作共享变量并且这些操作不全是只读操作那么它们彼此之间就会存在竞争这种状态就称为竞态。由于竞态的存在此时可能存在一个类在单线程的环境下能够正常运转但在多线程的环境下却运行出错此时就称这个类是线程不安全的。

public class J1_ThreadUnsafe {

    private static int i = 0;

    public static void main(String[] args) throws InterruptedException {
        IncreaseTask task = new IncreaseTask();
        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);
        thread1.start();
        thread2.start();
        // 等待线程结束再打印返回值
        thread1.join();
        thread2.join();
        System.out.println(i); // 线程不安全输出总是小于200000
    }

    static class IncreaseTask implements Runnable {
        @Override
        public void run() {
            for (int j = 0; j < 100000; j++) {
                inc();
            }
        }

        private void inc() {
            i++;
        }
    }
}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Java