多线程 2

1. Thread 类、常见方法

1.1 常用构造方法

方法 说明
Thread() 创建线程对象
Thread( Runnable target) 使用 Runnable 对象创建线程对象
Thread(String name) 创建线程对象并命名
Thread( Runnable target ,String name) 使用 Runnable 对象创建线程对象,并命名

1.2 线程的常见属性

属性 获取方法
ID getId()
名称 getNname()
状态 getState()
优先级 getPriority()
是否是后台线程 isDaemon()
是否存活 isAlive()
是否中断 isInterrupted()

获取线程的名称

java 复制代码
Thread t1 = new Thread();
//给线程起名字
Thread t2 = new Thread("线程2");
Thread t3 = new Thread(new Runnable(){
    @Override
    public void run(){
    }
},"线程3");
//getName 获取线程的名字
System.out.println(t1.getName());
System.out.println(t2.getName());
System.out.println(t3.getName());

线程的名字默认是 Thread - 数字, 给线程起名字,可以在线程数量很多的情况下,更方便地观察线程

1.3 前台线程和后台线程

  • 前台线程 : 会阻止进程的结束
  • 后台线程 : 不会阻止进程的结束
java 复制代码
public static void main(String[] args) throws InterruptedException {
   Thread t = new  Thread(() -> {
        for (int i = 0; i < 5; i++) {
            System.out.println("thread");
            try {
            		// 睡眠 1 秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("t线程结束");
    });
    t.start();
    // 睡眠 1 秒
    Thread.sleep(1000);
    System.out.println("主线程结束");
    // main 和 t 都是前台线程 2 个线程都结束 进程才结束
}

进程默认就是前台线程,可以通过 setDaemon(true) 把线程设置为 后台线程

参数是 true 表示是后台线程, 默认是 false 表示前台线程; 要在线程开启前设置

java 复制代码
public static void main(String[] args) throws InterruptedException {
    Thread t = new  Thread(() -> {
        for (int i = 0; i < 5; i++) {
            System.out.println("thread");
            try {
            		// 睡眠 1 秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println(" t 线程结束");
    });
    // 在 strat() 开始前 把 t 设置成 后台线程(守护线程)
    t.setDaemon(true);
    t.start();
    // 睡眠 1 秒
    Thread.sleep(1000);
    System.out.println("主线程结束");
    // 前台线程结束了 进程就结束
}

1.4 start( )

strart() 会调用操作系统的 API , 在操作系统中创造出一个线程

Java为了让一个 thread 对象和 一个操作系统线程 一一对应, 每个线程只能调用一次 start()

java 复制代码
public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        System.out.println("t1 thread");
    });
    t1.start();
    t1.start();
}

1.5 中断线程

中断线程的 2 种方式 :

  • 通过共享标记进行沟通
  • 通过调用 interrupt( ) 方法
使用自定义变量作为标识位
java 复制代码
private static boolean running = true;
public static void main(String[] args) {
    Thread t = new Thread(() -> {
        while(running){
            System.out.println("thread t");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    });
    t.start();
    try {
    	//睡眠 4 秒后 中断线程
          Thread.sleep(4000);
          running = false;
    }catch (InterruptedException e) {
          throw new RuntimeException(e);
    }
}
使用 interrupt( )
java 复制代码
public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(() -> {
    	// currentThread 获取 t 线程的引用
        Thread cur = Thread.currentThread();
        //isInterrupted 判断 t 线程的标识位
        while (!cur.isInterrupted()) {
            System.out.println("thread");
            try {
            	//预计 sleep 100 秒
                Thread.sleep(100_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t.start();
    Thread.sleep(1000);
    // 设置 t 线程的标识位
    t.interrupt();
}

interrupt() 能设置标志位,还能唤醒 sleep 等导致线程堵塞的方法

Q :为什么这里会在线程中止后又打印 thread ?

A : interrupt() 设置标识位为 true 唤醒 sleep() ,让 sleep() 抛出异常后, sleep() 自动重新设置标识位为 false
Thread.currentThread() : 获取当前线程的引用

java 复制代码
public static void main(String[] args) {
//获取主线程的引用
  Thread mainThread = Thread.currentThread();
  System.out.println(mainThread.getName());
}

如果在主线程执行 Thread.currentThread() 就是获取主线程的引用;

在thread1线程中执行 Thread.currentThread() 就是获取thread1线程的引用

1.6 join( ) 线程等待

java 复制代码
private static int sum = 0;
public static void main(String[] args) {
    Thread t = new Thread(() -> {
        for (int i = 1; i <= 1000; i++) {
            sum += i;
        }
    });

    t.start();
    System.out.println("sum = " + sum);
}

要求是 :等 t 线程计算完 1~1000 的和之后, 主线程再进行打印和

结果是 t线程还没有计算, 主线程就打印结果了,如果使用 sleep() 让 主线程睡一会,睡少了 t 线程还没有计算完,睡多了又浪费时间,另外如果 t 线程的逻辑复杂, sleep() 就更不适用,这时使用 join() 就能让一个线程执行结束后,再继续执行

方法 说明
join() 当前线程一直等待,直到目标线程执行完毕才继续
join( long millis ) 当前线程最多等待 millis 毫秒,超时后无论目标线程是否结束,当前线程都会继续执行
java 复制代码
private static int sum = 0;
public static void main(String[] args) throws InterruptedException {
   Thread t = new Thread(() -> {
       for (int i = 1; i <= 1000; i++) {
           sum += i;
       }
   });
   t.start();
   t.join();
   System.out.println("sum = " + sum);
}

join() 如果目标线程一直不结束,那就会一直等下去

java 复制代码
public static void main(String[] args) throws InterruptedException {
	//获取 main 线程的引用
     Thread main = Thread.currentThread();
     System.out.println("开始等待");
     main.join();
     System.out.println("线程结束");
}

join( long millis ) 等待设置的 ms 后继续执行

java 复制代码
public static void main(String[] args) throws InterruptedException {
	//获取 main 线程的引用
     Thread main = Thread.currentThread();
     System.out.println("开始等待");
     //等待 2 秒
     main.join(2000);
     System.out.println("线程结束");
 }

如果调用 join 前线程就已经执行完了,再次调用 join 是线程不会堵塞

2. 线程的状态

线程的 6 种状态

状态 表示
NEW 创建了 Thread 对象但没有 start()
TERMINATED 操作系统线程销毁(线程的执行入口执行完了),但 thread 对象还在
RUNNABLE 可运行的 1.CPU 正在执行 2.等待CPU调度执行(就绪)
WAITING 线程无限等待 调用无参 join(),目标线程不结束,等待进程的状态
TIMED_WAITING 线程有限等待 调用join(long millis) 过程中,等待进程的状态
BLOCKED 由于锁导致的堵塞
NEW 、RUNNABLE 、TERMINATED 状态的转换
java 复制代码
public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(() -> {
        
    });
    System.out.println(t.getState());
    t.start();
    System.out.println(t.getState());
    t.join();
    System.out.println(t.getState());
}
WAITING
java 复制代码
public static void main(String[] args) throws InterruptedException {
     Thread mainThread = Thread.currentThread();
     Thread t = new Thread(() -> {
         while(true){
             System.out.println("mainThread state " + mainThread.getState());
         }
     });
     t.start();
     t.join();
 }
TIMED_WAITING
java 复制代码
public static void main(String[] args) throws InterruptedException {
    Thread mainThread = Thread.currentThread();
    Thread t = new Thread(() -> {
        while(true){
            System.out.println("mainThread state " + mainThread.getState());
        }
    });
    t.start();
    t.join(10000);
}
相关推荐
daidaidaiyu10 小时前
ThingsBoard 集群的核心逻辑源码分析
java
我可能是个假开发11 小时前
FTP Unicode 文件名导致 `550` 的问题分析与解决方案
java·开发语言·spring
caoerzhong11 小时前
中小企业上 WMS 该先上哪几块:JeeWMS 开源 Java 仓库管理系统的分批上线清单
java·python·开源
【JAVA】玩家12 小时前
Spring核心原理全解析:从零到生产实战
java·后端·spring
Wang's Blog13 小时前
Java框架 SpringCloud 快速入门: 实现 Feign 最佳实践(抽取方式)
java·spring cloud
MandalaO_O14 小时前
IDEA 开发(快捷键 + 调试 + 序列化)
java·ide·intellij-idea
xiaoqiMikko14 小时前
JVM 线上排查实战(七):jps 看不见它、jstack 连不上它,可它明明活得好好的
java·jvm
狼爷15 小时前
Rust/Go/Java/Python/PHP 大比拼:负载下后端框架到底差多少?
java·后端·编程语言
念何架构之路15 小时前
zap扩展生态与总结
java·前端·数据库