ᕕ( ᐛ )ᕗ DawnStar's Blog

一、创建和运行线程

Java程序在启动时,都已经创建了一个主线程。

1. 方法一:直接使用Thread

// 创建线程对象,指定名字t1
Thread t = new Thread("t1"){
    public void run(){
        // 要执行的任务
        log.debug("hello");
    }
};
// 启动线程
t.start();
log.debug("hello main");

结果

image-20240725075318286

2. 方法二:使用Runnable配合Thread

把【线程】和【任务】(要执行的代码)分开

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        // 要执行的任务
        log.debug("running");
    }
};
// 创建线程对象
Thread t1 = new Thread(runnable, "t1");
// 启动线程
t1.start();

3. lambda简化

Java8以后使用lambda精简代码

Runnable task2 = () -> log.debug("task2");
Thread t2 = new Thread(task2, "t2");
t2.start();

4. Thread与Runnable的关系

5. 方法三:FutureTask配合Thread

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

// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
    log.debug("task3");
    return 100;
});

new Thread(task3, "t3").start();
// 主线程阻塞,同步等待task执行完毕的结果
Integer result = task3.get();
log.debug("result: {}", result);

二、观察多个线程同时运行

new Thread(() -> {
    while (true){
        log.debug("running");
    }
}, "t1").start();

new Thread(() -> {
    while (true){
        log.debug("running");
    }
}, "t2").start();

三、查看进程线程的方法

1. Windows

2. Linux

3. Java

四、线程运行的原理

1. 栈与栈帧

Java Virtual Machine Stacks (Java 虚拟机栈)

JVM中由堆、栈、方法去所组成。栈内存是给线程用的,每个线程启动后,虚拟机就会为其分配一块栈内存。

2. 线程上下文切换 Thread Context Switch

因为以下一些原因导致CPU不再执行当前的线程,转而执行另一个线程的代码

当Context Switch发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java中对应的概念就是程序计数器,它的作用是记住下一条JVM指令的执行地址,是线程私有的

五、常见方法

1. 常见方法

方法名static功能说明注意
start()启动一个新线程,在新的线程运行 run 方法中的代码start 方法只是让线程进入就绪,里面代码不一定立刻 运行(CPU 的时间片还没分给它)。每个线程对象的 start方法只能调用一次,如果调用了多次会出现 IllegalThreadStateException
run()新线程启动后会调用的方法如果在构造 Thread 对象时传递了 Runnable 参数,则线程启动后会调用 Runnable 中的 run 方法,否则默认不执行任何操作。但可以创建 Thread 的子类对象, 来覆盖默认行为
join()等待线程运行结束
join(long n)等待线程运行结 束,最多等待 n 毫秒
getId()获取线程长整型的 idid 唯一
getName()获取线程名
setName(String)修改线程名
getPriority()获取线程优先级
setPriority(int)修改线程优先级java中规定线程优先级是1~10 的整数,较大的优先级 能提高该线程被 CPU 调度的机率
getState()获取线程状态Java 中线程状态是用 6 个 enum 表示,分别为: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
isInterrupted()判断是否被打 断, 不会清除打断标记
isAlive()线程是否存活(还没有运行完毕)
interrupt()打断线程如果被打断线程正在 sleep,wait,join 会导致被打断 的线程抛出 InterruptedException,并清除打断标记 ;如果打断的正在运行的线程,则会设置打断标记 ;park 的线程被打断,也会设置打断标记
interrupted()static判断当前线程是否被打断会清除打断标记
currentThread()static获取当前正在执
sleep(long n)static让当前执行的线 程休眠n毫秒, 休眠时让出 cpu 的时间片给其它 线程
yield()static提示线程调度器 让出当前线程对CPU的使用 主要是为了测试和调试

2. sleep 与 yield

sleep

yield

3. 线程优先级

Runnable task1 = () -> {
    int count = 0;
    for (;;){
        System.out.println("----->1     "+count++);

    }
};
Runnable task2 = () -> {
    int count = 0;
    for (;;){
        Thread.yield();
        System.out.println("     ----->2    "+count++);
    }
};
Thread t1 = new Thread(task1, "t1");
Thread t2 = new Thread(task2, "t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();

4. sleep应用-防止CPU占用100%

sleep实现

在没有利用CPU来计算时,不要让while(true)空转浪费CPU,这时可以使用yeild或者sleep来让出CPU的使用权给其他程序。

while (true){
    try{
        Thread.sleep(50);
    }catch (InterruptedException e){
        e.printStackTrace();
    }
}

5. join

static int r = 0;
public static void main(String[] args) throws InterruptedException {
    test1();
}
public static void test1() throws InterruptedException {
    log.info("开始");
    Thread t1 = new Thread(() -> {
        log.info("t1 开始");
        try {
            sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("t1 结束");
        r = 10;
    } , "t1");
    t1.start();
    // t1.join(); // 让线程对象加入到主线程中
    log.info("r = {}", r);
    log.info("结束");
}

#Java