首页 > 编程语言 >1_JAVA线程

1_JAVA线程

时间:2024-06-11 18:32:29浏览次数:13  
标签:JAVA Thread void t1 线程 new public

Java 线程

1. 创建和运行线程

1.1 直接使用 Thread

例如:

public class ThreadTest {
    public static void main(String[] args) {
        Thread t = new Thread() {
            public void run(){
                // 要执行的任务
                System.out.println(Thread.currentThread().getName()+" Running");
            }
        };
        // 启动线程
        t.setName("thread1-");	// 给线程起名字
        t.start();
        System.out.println(Thread.currentThread().getName());
    }
}
main
thread1- Running

1.2 使用Runnable 配合 Thread

将线程的创建和要执行的任务分开

  • Thread 代表要执行的线程
  • Runnable 可运行的任务[线程要执行的代码]

例如:

public class ThreadTest {
    public static void main(String[] args) {
        // 编写一个人任务
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"-"+"hello");
            }
        };
        // 创建一个线程,将任务交给线程
        Thread t = new Thread(task);
        t.setName("task-thread");
        t.start();
        System.out.println(Thread.currentThread().getName());
    }
}
main
task-thread-hello

1.3 Thread 和 Runnable 的联系

  1. 直接使用Thread 创建线程是将创建线程和任务逻辑编写在了一起,而Thread配合Runnable可以将任务逻辑和线程创建分割开来。
  2. 用Runnable 更容易与线程池等高级 API 进行配合。
  3. 用Runnable 让任务脱离了 Thread 继承体系,更灵活。
  4. Thread 继承了 Runnable 接口

1.4 FutureTask 配合 Thread

FutureTask 能够接收 Callable 类型的而参数,用来处理有返回结果的情况,例如:

public class ThreadTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // FutureTask中的范形就是将来要返回结果的类型
        FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName()+" running...");
                Thread.sleep(1000);
                // 返回结果
                return 10086;
            }
        });

        Thread t = new Thread(task);
        t.setName("task-thread");
        t.start();
        // 使用主线程接收子线程运行后的结果
        System.out.println(Thread.currentThread().getName()+"线程接收到了"+t.getName()+"线程执行后的结果:"+task.get());
    }
}
task-thread running...
main线程接收到了task-thread线程执行后的结果:10086

2. 查看多个线程同时运行

编写一个还有多个线程的实例:

public class TestMultiThread {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println(Thread.currentThread().getName()+" running...");
                }
            }
        },"thread1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println(Thread.currentThread().getName()+" running...");
                }
            }
        },"thread2").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println(Thread.currentThread().getName()+" running...");
                }
            }
        },"thread3").start();
    }
}

运行的部分结果如下:

...
thread1 running...
thread1 running...
thread1 running...
thread3 running...
thread3 running...
thread3 running...
thread3 running...
thread2 running...
thread2 running...
thread1 running...
......

由结果看出,线程运行的先后顺序是无法确定的,这取决于CPU对线程的调度。

3. 查看进程线程的方法

在不同的操作系统下查看线程进程是不一样的。

3.1 Windows

  • 打开任务管理器就可以查看线程和进程数,也可以用来杀死进程。

  • **tasklist **查看进程

  • **taskkill **杀死进程

3.2 Linux

  • **ps -fe **查看所有进程

  • **ps -fT -p : **产看某个进程的所有线程

  • kill :杀死进程

  • **top: **按照大写H 切换是否显示线程\

3.3 Java

  • **jps: **命令查看所有java进程
  • jstack 查看某个Java 进程(PID)的所有线程状态
  • **jconsole: **查看某个Java进程中线程的运行情况

4. 线程运行的原理

4.1 栈与栈帧

Java Virtual Machine Stacks(Java 虚拟机栈)

我们都知道 JVM 中由堆,栈,方法区所组成,其中的栈内存是给线程使用的,每个线程启动后,虚拟机就会分配一块栈内存。

  • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存。
  • 每个线程都只能有一个活动栈帧,对迎着当前正在执行的那个方法

4.2 线程上下文切换(Thread Context Switch)

因为以下原因导致cpu不能再继续执行当前线程,转而去执行另外一个线程的代码:

  • 当前线程的时间片用完
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了 sleep, yield, wait, join, park, synchronized, lock等方法

当上下文发生切换时,需要操作系统保存当前线程的信息,并恢复另外一个线程的状态继续执行。Java对应的概念就是程序计数器,它的作用就是记住下一条JVM指令的执行地址,是线程私有的。

  • 状态包括程序计数器,虚拟机栈中的每个栈帧信息,如局部变量表,操作数栈,返回地址等。
  • 发生上下文切换太频繁会影响性能

5. Java线程常见的方法

方法名 static 功能说明 注意
start() 启动一个新线程,在新的线程运行run方法中的代码 start方法只是让线程处于就绪态,里面的代码不一定会立刻执行,需要等到线程调度器将CPU时间片分配给该线程才会运行。该方法只会调用一次,调用多次会抛出非法线程状态异常
run() 信线程启动后会调用的方法(一般是自己编写的逻辑)
join() 等待线程运行结束
join(long n) 等待线程运行结束, 最多等待 n ms
getId() 获取线程ID 线程ID是唯一的
getName() 获取线程名字
setName(String) 设置线程名字
getPriority() 获取线程优先级 java 规定线程的而优先级是 1-10 的整数,比较大的优先级会提高该线程会被CPU优先调度
setPriority(int) 设置线程优先级
getState() 获取线程状态 Java中的线程状态一共有6种(用枚举表示):NEW, RUNNABLE, BLOCKED, WAITTING, TIMED_WAITTING, TERMINATED
isInterrupted() 判断是否被打断 不会清除打断标记
isAlive 判断线程是否存活(还没有运行完毕)
interrupt() 打断线程 打断当前线程处于运行的状态,并设置打断标记
isInterrupt() 判断当前线程是否被打断
currentThread 获取当前正在运行的线程
sleep(long n) 让当前正在运行的线程暂时放弃CPU的使用权
yield() 提示线程调度器让出当前运行的线程,释放出CPU资源 主要是为了测试和调试

6. start 与 run

start表示启动一个线程,而run是线程启动后要执行的逻辑代码。

那么我们能不能不经过start而直接调用run方法呢?例如下面的代码:

public class TestThread4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行run方法");
            }
        },"thread");
        t.run();
    }
}

运行结果如下:

main执行run方法

Process finished with exit code 0

我们发现:run()方法确实被执行的,但是是由主方法调用的run()方法,而不是子线程调用的run()方法!

7. sleep 与 yield

7.1 sleep

  1. 调用sleep 会让当前线程从RUNNING 进入到 TIMED_WAITTING 状态
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出打断异常
  3. 睡眠结束后的线程未必会立刻执行,状态只是从 TIMED_WAITTING 进入到
  4. 建议使用 TimeUnit 的sleep 来代替 Thread 的 sleep 开火的更好的可读性

调用sleep 进 TIMED_WAITTING 状态:

public class ThreadTest5 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getState());
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"thread1");

        System.out.println(t1.getState());
        t1.start();
    }
}

正在睡眠的线程可以被其它线程打断:

public class ThreadTest5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("t1线程进入睡眠...");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    System.out.println("线程t1被线程main打断唤醒");
                    throw new RuntimeException(e);
                }
            }
        },"t1");

        t1.start();
        Thread.sleep(1000);
        System.out.println("打断线程t1");
        t1.interrupt();
    }
}

运行结果如下:

t1线程进入睡眠...
打断线程t1
线程t1被线程main打断唤醒
Exception in thread "t1" java.lang.RuntimeException: java.lang.InterruptedException: sleep interrupted
	at com.cherry.a03.ThreadTest5$1.run(ThreadTest5.java:18)
	at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep0(Native Method)
	at java.base/java.lang.Thread.sleep(Thread.java:509)
	at com.cherry.a03.ThreadTest5$1.run(ThreadTest5.java:15)
	... 1 more

Process finished with exit code 0

TimeUnit 的sleep 相比于 Thread.sleep 更好一些,好在 TimeUnit 可以指定时间的单位(把时间分成了不同的单位),可读性更好:

public enum TimeUnit {
    /**
     * Time unit representing one thousandth of a microsecond.
     */
    NANOSECONDS(TimeUnit.NANO_SCALE),
    /**
     * Time unit representing one thousandth of a millisecond.
     */
    MICROSECONDS(TimeUnit.MICRO_SCALE),
    /**
     * Time unit representing one thousandth of a second.
     */
    MILLISECONDS(TimeUnit.MILLI_SCALE),
    /**
     * Time unit representing one second.
     */
    SECONDS(TimeUnit.SECOND_SCALE),
    /**
     * Time unit representing sixty seconds.
     * @since 1.6
     */
    MINUTES(TimeUnit.MINUTE_SCALE),
    /**
     * Time unit representing sixty minutes.
     * @since 1.6
     */
    HOURS(TimeUnit.HOUR_SCALE),
    /**
     * Time unit representing twenty four hours.
     * @since 1.6
     */
    DAYS(TimeUnit.DAY_SCALE);
	......
}

简单用法如下:

public class ThreadTest5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 子线程睡眠2s
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"t1");

        t1.start();
    }
}

7.2 yield

  • 调用 yield 会让当前线程从Running进入到 Runnable 就绪状态,然后调度执行其它线程
  • 具体的实现依赖于操作系统的线程调度

8. 线程优先级

  • 线程优先级会提示(hint) 调度器优先调度该线程,但它仅仅只是一个提示,调度器可以忽略它
  • 如果CPU比较繁忙,那么优先级高的线程会获得更多的时间片,但CPU空闲时,优先级几乎没作用

不加yield:

public class yieldTest1 {
    public static void main(String[] args) {
        Runnable task1 = new Runnable() {
            @Override
            public void run() {
                int t1_count = 0;
                for(;;){
                    System.out.println("--->"+Thread.currentThread().getName()+" "+(t1_count++));
                }
            }
        };

        Runnable task2 = new Runnable() {
            @Override
            public void run() {
                int t2_count = 0;
                for(;;){
                    System.out.println("        --->"+Thread.currentThread().getName()+" "+(t2_count++));
                }
            }
        };

        Thread t1 = new Thread(task1,"thread1");
        Thread t2 = new Thread(task2,"thread2");
        t1.start();
        t2.start();
    }
}

运行结果部分如下:

       --->thread2 54537
        --->thread2 54538
--->thread1 50531
--->thread1 50532
        --->thread2 54539
        --->thread2 54540

我们发现不是用yield两个线程的计算机结果是差不多的。

接下来我们使用yield再次运行:

public class yieldTest1 {
    public static void main(String[] args) {
        Runnable task1 = new Runnable() {
            @Override
            public void run() {
                int t1_count = 0;
                for(;;){
                    System.out.println("--->"+Thread.currentThread().getName()+" "+(t1_count++));
                }
            }
        };

        Runnable task2 = new Runnable() {
            @Override
            public void run() {
                int t2_count = 0;
                for(;;){
                    Thread.yield();	// 让出t2线程 --> 线程t1会占用大部分的时间片,而线程t2只会占用少部分时间片
                    // yield 让出线程并不一定会成功,最终还是操作系统的线程调度器考虑究竟是否让出线程
                    System.out.println("        --->"+Thread.currentThread().getName()+" "+(t2_count++));
                }
            }
        };

        Thread t1 = new Thread(task1,"thread1");
        Thread t2 = new Thread(task2,"thread2");
        t1.start();
        t2.start();
    }
}
--->thread1 91806
--->thread1 91807
--->thread1 91808
--->thread1 91809
--->thread1 91810
        --->thread2 41088
        --->thread2 41089

接着我们不使用yield让出线程,而是使用线程优先级查看一下线程调度:

public class yieldTest1 {
    public static void main(String[] args) {
        Runnable task1 = new Runnable() {
            @Override
            public void run() {
                int t1_count = 0;
                for(;;){
                    System.out.println("--->"+Thread.currentThread().getName()+" "+(t1_count++));
                }
            }
        };

        Runnable task2 = new Runnable() {
            @Override
            public void run() {
                int t2_count = 0;
                for(;;){
                    System.out.println("        --->"+Thread.currentThread().getName()+" "+(t2_count++));
                }
            }
        };

        Thread t1 = new Thread(task1,"thread1");
        Thread t2 = new Thread(task2,"thread2");
        t1.setPriority(Thread.MIN_PRIORITY);    // 线程1设置最低优先级
        t2.setPriority(Thread.MAX_PRIORITY);    // 线程2设置最高优先级
        t1.start();
        t2.start();
    }
}
--->thread1 68186
--->thread1 68187
--->thread1 68188
--->thread1 68189
        --->thread2 74237
        --->thread2 74238
        --->thread2 74239
        --->thread2 74240

我们发现,这优先级其实并不明显,因为哪个线程优先被分配时间片最终还是取决于操作系统的线程调度器。

9. sleep 应用

9.1 sleep实现

在没有利用 cpu 计算时,不要使用 while(true) 空转浪费 cpu 资源,这时可以使用 yield 或者 sleep 让出 cpu 资源分配给其它线程或进程:

package com.cherry.a03;

/**
 * @author: lily
 * @data: 2024.06.11
 * @description:
 */
public class SleepTest1 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    
                }
            }
        });
        t1.start();
    }
}

如果不使用Thread.sleep(xxx),子线程就会一直占用CPU资源,如果该线程跑在单核CPU上,会瞬间将CPU跑满,其它线程就不会得到CPU资源。因此,最好是在while(true)中加入sleep, 休眠时间大小可自行设置,避免空转导致占满了CPU资源。

10. join 方法解析

join()方法是等待线程运行结束。首先观察如下代码:

public class Test10 {

    private static int count = 0;
    public static void main(String[] args) {
        test1();
    }

    public static void test1(){
        System.out.println(Thread.currentThread().getName()+"开始");
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"开始");
                try {
                    sleep(1000);
                    count = 10;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    System.out.println(Thread.currentThread().getName()+"结束");
                }
            }
        },"thread1");
        t1.start();
        System.out.println("count结果为:"+count);
        System.out.println(Thread.currentThread().getName()+"结束");
    }
}

结果为:

main开始
thread1开始
count结果为:0
main结束
thread1结束

Process finished with exit code 0

我们发现,由于主线程和子线程是并行运行的,子线程还没运行完主线程就已经运行结束了。

如果我们要等待子线程运行完成后,才能让主线程完成运行,这就需要我们主线程要等待子线程运行,可以在 t1.start() 后使用 join() 方法。如下所示:

public class Test10 {

    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        test1();
    }

    public static void test1() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+"开始");
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"开始");
                try {
                    sleep(1000);
                    count = 10;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    System.out.println(Thread.currentThread().getName()+"结束");
                }
            }
        },"thread1");
        t1.start();
        // 等待 t1 线程运行结束
        t1.join();
        System.out.println("count结果为:"+count);
        System.out.println(Thread.currentThread().getName()+"结束");
    }
}

运行结果如下:

main开始
thread1开始
thread1结束
count结果为:10
main结束

Process finished with exit code 0

10. 1 应用之同步

从调用者的角度来讲:

  • 需要等待结果返回,才能继续运行就是同步
  • 不需要等待结果返回,就能继续运行就是异步

image-20240611150635995

如果我不止想等待一个程序的运行结果,而是多个程序的运行结果,那就调用不同线程的join方法,例如:

public class Test11 {
    private static int a;
    private static int b;
    private static int c;
    public static void main(String[] args) throws InterruptedException {
        Runnable task1 = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread()+"开始运行...");
                try {
                    Thread.sleep(1000);
                    a = 10;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    System.out.println(Thread.currentThread()+"结束运行...");
                }
            }
        };

        Runnable task2 = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread()+"开始运行...");
                try {
                    Thread.sleep(2000);
                    b = 20;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    System.out.println(Thread.currentThread()+"结束运行...");
                }
            }
        };

        Runnable task3 = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread()+"开始运行...");
                try {
                    Thread.sleep(3000);
                    c = 30;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    System.out.println(Thread.currentThread()+"结束运行...");
                }
            }
        };

        Thread t1 = new Thread(task1,"thread1");
        Thread t2 = new Thread(task2,"thread2");
        Thread t3 = new Thread(task3,"thread3");

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();

        System.out.println(a+b+c);
    }
}

运行结果如下:

Thread[#22,thread2,5,main]开始运行...
Thread[#21,thread1,5,main]开始运行...
Thread[#23,thread3,5,main]开始运行...
Thread[#21,thread1,5,main]结束运行...
Thread[#22,thread2,5,main]结束运行...
Thread[#23,thread3,5,main]结束运行...
60

Process finished with exit code 0

10.2 有时效的join

join(long n): 线程最多会等待 n ms,如果等待的时间超过了n ms, 就不会继续等下去。例如:

public class Test12 {
    private static int count = 10;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    count = 30;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();
        t.join(1000);
        System.out.println(count);
    }
}

运行结果如下:

10

Process finished with exit code 0

子线程需要休眠3秒才能拿到结果,但是子线程只能等待1秒,因此子线程等待结束后并不会拿到最终的结果!

11. interrupt 方法详解

11.1 打断 sleep, wait, join的线程

打断 sleep 线程,会清空打断状态,以 sleep 为例:

public class SleepTest3 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"thread1");
        t1.start();

        TimeUnit.MILLISECONDS.sleep(100);
        t1.interrupt(); // 打断线程t1的 sleep 状态
        System.out.println("打断状态:"+t1.isInterrupted());
    }
}
public class SleepTest3 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"thread1");
        t1.start();

        TimeUnit.MILLISECONDS.sleep(100);
        t1.interrupt(); // 打断线程t1 sleep 状态
        System.out.println("打断状态:"+t1.isInterrupted());
    }
}
Exception in thread "thread1" java.lang.RuntimeException: java.lang.InterruptedException: sleep interrupted
	at com.cherry.a03.SleepTest3$1.run(SleepTest3.java:20)
	at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep0(Native Method)
	at java.base/java.lang.Thread.sleep(Thread.java:558)
	at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)
	at com.cherry.a03.SleepTest3$1.run(SleepTest3.java:18)
	... 1 more
打断状态:true

Process finished with exit code 0

打断正常运行的线程,不会清空打断状态:

public class ThreadTest15 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    if(Thread.currentThread().isInterrupted()){
                        System.out.println("正在运行的线程被打断了,exit");
                        break;
                    }
                }
            }
        });
        t1.start();
        TimeUnit.MILLISECONDS.sleep(100);
        System.out.println("interrupt");
        t1.interrupt();
    }
}

运行结果如下:

interrupt
正在运行的线程被打断了,exit

Process finished with exit code 0

11.2 两阶段终止模式

如何在一个线程中优雅的终止另一个线程

错误思想:

  1. 使用线程对象的stop()方法:stop()方法会真正的杀死线程,如果此时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁了,其他线程将永远无法获得该资源的锁。
  2. 使用 System.exit() 方法停止线程:目的仅仅是停止一个线程,但这种做法会让整个程序(进程)都停止。

正确的多线程终止应如下所示:

image-20240611160116390

代码实现如下:

public class Test04 {
    public static void main(String[] args) throws InterruptedException {
        Monitor monitor = new Monitor();
        monitor.start();

        // 主线程过3500ms后将监控程序优雅的停止下来
        Thread.sleep(3500);
        monitor.stop();
    }
}

// 监控类
class Monitor {
    private Thread monitorThread;   // 监控线程

    // 启动监控线程
    public void start(){
        monitorThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    // 获取当前线程的打断状态
                    Thread t1 = Thread.currentThread();
                    if(t1.isInterrupted()){
                        // 如果被打断
                        System.out.println("料理后世");
                        break;
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1);
                        System.out.println("执行监控记录");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        // 重新设置打断标记
                        Thread.currentThread().interrupt();
                    }
                }
            }
        });

        monitorThread.start();
    }

    // 停止监控线程
    public void stop(){
        monitorThread.interrupt();
    }
}

运行结果如下:

执行监控记录
执行监控记录
执行监控记录
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep0(Native Method)
	at java.base/java.lang.Thread.sleep(Thread.java:558)
	at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)
	at com.cherry.a03.Monitor$1.run(Test04.java:39)
	at java.base/java.lang.Thread.run(Thread.java:1583)
料理后世

Process finished with exit code 0

12. 主线程与守护线程

默认情况下,Java 进程需要等待所有线程都运行结束,Java进程才会停止运行,有一种特殊的线程叫做守护线程,只有它非守护线程运行都运行结束了,即使守护线程的代码没有执行完,也会强制结束。

例如:

public class Test6 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    if(Thread.currentThread().isInterrupted()){
                        break;
                    }
                }
            }
        });
        t1.setDaemon(true); //设置t1线程为守护线程
        t1.start();

        Thread.sleep(1000);
        System.out.println("结束");
    }
}

注意:

  • 垃圾回收器就是一种守护线程
  • Tomact中的Acceptor 和 Poller 线程也都是守护线程

13 Java层面的线程状态

根据 ThreadState 枚举,分为6种状态:

image-20240611173953437

  • NEW 线程刚被创建,但是还没调用start()方法
  • RUNNABLE: 当调用了start()方法后,注意,Java API层面的runnable包含了操作系统层面的[就绪态],[运行态], [阻塞态]。
public class TestState {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("running...");
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    // runnable
                }
            }
        });
        t2.start();

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("running...");   // terminated
            }
        });
        t3.start();

        Thread t4 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (TestState.class){
                    try{
                        Thread.sleep(1000000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        t4.start();

        Thread t5 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    t2.join();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t5.start();

        Thread t6 = new Thread(new Runnable() {
            @Override
            public void run() {
               synchronized (TestState.class){  // 没办法拿到锁 BLOCK
                   try {
                       Thread.sleep(100000);
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }
               }
            }
        });
        t6.start();

        try{
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("t1 state:"+t1.getState());
        System.out.println("t2 state:"+t2.getState());
        System.out.println("t3 state:"+t3.getState());
        System.out.println("t4 state:"+t4.getState());
        System.out.println("t5 state:"+t5.getState());
        System.out.println("t6 state:"+t6.getState());
    }
}

结果如下:

running...
t1 state:NEW
t2 state:RUNNABLE
t3 state:TERMINATED
t4 state:TIMED_WAITING
t5 state:WAITING
t6 state:BLOCKED

标签:JAVA,Thread,void,t1,线程,new,public
From: https://www.cnblogs.com/lilyflower/p/18242555

相关文章

  • 【Java】输出填空 异常捕获
    classNoWaterextendsException{}classNoDrinkableWaterextendsNoWater{}publicclassFinallyWorks{staticintcount=0;publicstaticvoidmain(String[]args)throwsNoWater{while(true){try{count++;......
  • 【Java】7-1 职工排序题 分数 20
    1.为某保险公司设计一个职工管理系统,其中职工类的属性有:职工编号,姓名,性别,团体险业绩,个体险业绩;方法有:每个属性对应的set,get方法;不带参数的构造方法;带参数的构造方法,完成对职工属性的初始化;该类实现接口Comparable,完成对职工总业绩的比较。2.设计一个类,实现Comparator接口,......
  • Java环境配置及配置过程遇到的问题
    配置过程安装完jdk之后,找到jdk的安装目录首先配置JAVA_HOME新建一个系统变量,变量值为jdk的路径再配置pathpath中新建变量值内容为%JAVA_HOME%\bin原因:因为jdk目录下的bin文件夹中存放着java和javac等的exe文件配置完之后打开新的cmd窗口即可javajavac能够显示出用法即可......
  • 深入理解代码耦合度:从无直接耦合到内容耦合(JAVA案例)
    目录前言第一点:无直接耦合第二点:数据耦合第三点:标记耦合第四点:控制耦合第五点:外部耦合第六点:公共耦合第七点:内容耦合总结前言在软件开发中,代码的耦合度是一个非常重要的概念。理解不同类型的耦合度对于设计和维护高质量的代码至关重要。本文将介绍从无直接耦合到......
  • Java数据结构与算法(回溯算法)
    前言回溯算法是一种通过构建问题的解树(或解图)来逐步构建候选解的通用算法。它尝试通过一系列选择来解决问题,选择可能包括移动、添加一个元素到当前解、决定一个解的某部分等。当发现某个选择无法导致一个有效解时,算法会回退(即回溯),撤销该选择,并尝试其他选择。回溯算法通常用于......
  • JavaScript中什么是类,如何使用?
    在JavaScript中,类是一种用于创建对象的模板。它定义了对象的属性和方法,并可以通过实例化来创建具体的对象。类提供了一种结构化的方式来组织和管理代码,使得代码更易于理解和维护。下面我将通过三个例子来详细说明JavaScript中类的概念和使用方法。例子1:创建一个表示人的类cl......
  • Java多线程(一):多线程基础
    多线程技术概述线程与进程进程:一个内存中运行的应用程序每个进程有一个独立的内存空间线程进程中的一个执行路径·共享一个内存空间·线程间自由切换,并发执行·一个进程至少有一个线程·线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干......
  • 初阶java学习2
    Notepad软件高级记事本有行号,而且Java中的一些特殊单词会高亮显示方便我们对报错进行修改;常见的高级记事本Editplus、Notepad++、Sublime(前端程序员常用)等Notepad++下载方式百度网盘:百度网盘请输入提取码提取码:e36o编码选择ANSI可以让我们输出中文;JAVA的三......
  • 基于Vue+Node.js的高校学业预警系统+10551(免费领源码)可做计算机毕业设计JAVA、PHP、爬
    NodeJS高校学业预警系统摘 要随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,教育行业当然也不能排除在外。高校学业预警系统是以实际运用为开发背景,运用软件工程开发方法,采用Node.JS技术构建的一个管理系统。......
  • JavaSE中的IO(输入/输出)字节流字符流
    JavaSE中的IO(输入/输出)知识是一个广泛的领域,它涵盖了如何在Java程序中进行数据的读取和写入。以下是对JavaSE中IO知识的一个清晰归纳:一、基础知识流(Stream)的概念流是一组有顺序的、有起点和终点的字节集合,用于数据传输。Java的I/O流提供了读写数据的标准方法。Java的I/O......