Java中线程常见方法

start()

启动一个线程,再新线程中运行run方法
image.pngstart只能让它就绪,运不运行由任务调度器决定

run()

新线程启动后调用的方法
image.png

start与run

直接执行run是用主线程执行的

package com.zqh.day1;

import lombok.extern.slf4j.Slf4j;

/**
 * Start与Run
 */
@Slf4j
public class StartVSRun {
    // 主线程
    public static void main(String[] args) throws InterruptedException {
        // t1线程
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                log.debug("running");
            }
        };
        // 直接执行run方法,是main线程执行的,没有启动新的线程
        // 不能达到异步处理,提升性能等效果
        t1.run();
    }
}

image.png
不会创建新线程

Join()/Join(long n)

用于等待某个线程运行结束,用于两个线程通信
比如有一个线程t1运行了一段时间,最后会有一个结果。
这时主线程想拿到这个结果,此时主线程就可以调用主线程的Join方法进行等待,等待t1线程运行完
image.png
image.png

Join案例

图片.png
最后打印的r是多少?
不是10图片.png是0

分析执行流程:
主线程中主方法执行test1,打印了第一个开始:图片.png
与此同时,线程一启动了:图片.png
再打印线程1的开始:图片.png,接下来,线程一睡了图片.png而主线程就继续往下运行了,不会等,所以主线程直接取结果,为0。图片.png一秒以后线程1行了,输出他的结束,再去赋值,这时r才变成10

因为主线程和线程一是并行执行的,而t1线程需要睡一秒才能算出r=10,主线程又一直要执行,所以只能打印出r=0。

解决方法:
如何在主线程获取r=10?
使用Join方法,具体为要等待哪个线程运行结束,就用哪个线程的join方法

package com.zqh.day1;

import lombok.extern.slf4j.Slf4j;

/**
 * Description Join 方法,用于等待一个线程运行完
 * date 2023/10/24 23:05
 *
 * @author zqh
 * @since JDK 1.8
 */
@Slf4j(topic = "c.Join")
public class Join {
    // 设置静态变量r
    static int r = 0;
    // 主线程
    public static void main(String[] args) throws InterruptedException {
        // 运行test1方法
        test1();
    }

    private static void test1() throws InterruptedException {
        log.debug("开始");
        Thread t1 = new Thread(){
            @Override
            public void run() {
                setName("t1");
                // t1线程中的任务
                log.debug("开始");
                // 睡1秒
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.debug("结束");
                r = 10;
            }
        };
        t1.start();
        // 主线程要等待t1线程执行完获取r的结果,所以用t1线程的join方法
        t1.join();
        // 用主线程获取r
        log.debug("结果为:{}",r);
    }
}

应用-同步

从调用方来讲,同步就是需要等待返回结果,才能继续运行就是同步
不需要等待返回结果,就能继续运行就是异步
主线程同步等待t1线程
图片.png

这是主线程等待一个线程,那么主线程等待多个线程如何做呢?
调用不同线程的Join方法
图片.png
等待了两秒
图片.png
重点:两个线程是同时启动的

有时效的Join(long n)

最多等待n毫秒,超过时间就不等待,继续往下运行

package com.zqh.day1;

import lombok.extern.slf4j.Slf4j;

/**
 * Description Join 方法,用于等待一个线程运行完
 * date 2023/10/24 23:05
 *
 * @author zqh
 * @since JDK 1.8
 */
@Slf4j(topic = "c.Join")
public class Join {
    // 设置静态变量r
    static int r = 0;
    // 主线程
    public static void main(String[] args) throws InterruptedException {
        // 运行test1方法
        test1();
    }

    private static void test1() throws InterruptedException {
        log.debug("开始");
        Thread t1 = new Thread(){
            @Override
            public void run() {
                setName("t1");
                // t1线程中的任务
                log.debug("开始");
                // 睡1秒
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.debug("结束");
                r = 10;
            }
        };
        t1.start();
        // 主线程要等待t1线程执行完获取r的结果,所以用t1线程的join方法
        t1.join(500);
        // 用主线程获取r
        log.debug("结果为:{}",r);
    }
}

Sleep与yield

Sleep

图片.png
Sleep可以让线程睡眠,睡眠时间相当于放弃了CPU时间片的使用,由Running进入TIMEDWAITING状态

sleep案例

image.png
为什么t1是runnable呢,可能是因为t1还没睡之前,main线程就执行到getstate方法了。我们让主线程也休眠一下(sleep在哪个线程被调用哪个线程就被休眠)

package com.zqh.day1;

import lombok.extern.slf4j.Slf4j;

/**
 * Sleep方法
 */
@Slf4j
public class SleepThread {
    // 主线程
    public static void main(String[] args) {
        // t1线程
        Thread t1 = new Thread(){
            @Override
            public void run() {
                // t1线程睡2秒
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        // t1线程启动后直接睡两秒
        t1.start();
        // 这里会显示Runnable,因为主线程执行的太快了
        log.debug("t1的状态是:{}",t1.getState());
        // 主线程也睡,睡的比t1少一点,不然看不到t1状态
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // 再看t1状态
        log.debug("t1的状态是:{}",t1.getState());
    }
}

image.png

打断睡眠案例

其他线程可以用睡眠线程的interrupt方法打断正在睡眠的线程,这时sleep方法会抛异常

package com.zqh.day1;

import lombok.extern.slf4j.Slf4j;

/**
 * Sleep方法
 */
@Slf4j
public class InterruptSleepThread {
    // 主线程
    public static void main(String[] args) throws InterruptedException {
        // t1线程
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                log.debug("enter sleep..");
                // t1线程睡2秒
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    // 记录唤醒
                    log.debug("wake up..");
                    e.printStackTrace();
                }
            }
        };
        // t1线程启动后直接睡
        t1.start();
        // 主线程在一秒以后唤醒t1线程
        Thread.sleep(1000);
        log.debug("interrupt");
        // 在main线程调用t1线程的interrupt方法唤醒t1线程
        t1.interrupt();

    }
}

image.png
看结果,t1睡了,本来打算睡两秒,但是在睡了一秒之后就被主线程打断了

Sleep应用:防止CPU占用100%

场景:服务器部署了一个项目,要让他一直运行,可能会写死循环while(true)
图片.png写一个sleep会防止CPU占用100%
图片.png

yield

调用该方法会让当前线程从Running进入Runnable(就绪)状态,然后让其他线程使用CPU时间片**,依赖于任务调度器

先看看yield与sleep的区别

两个方法都是让一个线程不要运行,把CPU时间片让给另一个线程运行,但是调用方法后,原先运行的线程的状态不同,调用Sleep后线程转变成TIMEDWAITING,是一种阻塞状态,调用yield后,线程转变成Runnable就绪状态,这个状态还是可能被分配时间片的。

线程优先级

线程可以用setpriority设置优先级,默认为5,数字越大,优先级越高,
图片.png例子:
图片.png
两个线程t1,t2,两个Runnable任务,都是死循环打印自增数字

Interrupt方法

打断阻塞状态(sleep、wait、join)的线程

打断哪个线程就用哪个线程的interrupt方法,阻塞状态的线程被打断会抛异常InterruptedException
被打断的线程会有一个打断标记,表示这个线程被其他线程打断,是一个布尔值。被打断是true,没被打断是false但是sleep、wati、join被打断后会把打断标记清空(存疑?)

package com.zqh.day1;

import lombok.extern.slf4j.Slf4j;

/**
 * Description Interrupt方法打断阻塞线程
 * date 2023/10/24 23:34
 *
 * @author zqh
 * @since JDK 1.8
 */
@Slf4j(topic = "c.Interrupt")
public class Interrupt {
    // 主线程
    public static void main(String[] args) throws InterruptedException {
        // t1线程
        Thread t1 = new Thread(()->{
            log.debug("sleep...");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"t1");
        t1.start();
        Thread.sleep(1000);
        log.debug("interrupt...");
        // t1线程状态
        log.debug("t1线程的状态:{}",t1.getState());
        t1.interrupt();
        // 打断标记,打断阻塞线程,标记会清空(false)
        //todo 为什么有时不是false
        log.debug("打断标记为:{}",t1.isInterrupted());
    }
}

打断运行状态的线程

打断运行状态的线程,不会让线程停下来,需要靠打断标记。

package com.zqh.day1;

import lombok.extern.slf4j.Slf4j;

/**
 * Description 打断正常运行线程
 * date 2023/10/24 23:43
 *
 * @author zqh
 * @since JDK 1.8
 */
@Slf4j
public class Interrupt2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            while (true){
                if (Thread.currentThread().isInterrupted()){
                    log.debug("被打断啦");
                    break;
                }
            }
        });
        // t1启动
        t1.start();
        // 主线程睡一秒,这一秒可以看出t1是死循环
        Thread.sleep(1000);
        // 打断
        t1.interrupt();
    }
}

打断Park线程

park是LockSupport类中的一个方法,他的作用也是让当前线程停下来
图片.png打印标记为真的情况下park方法会失效,此时可以使用interrupted这个方法,与interrupt不同的是,这个方法返回打断标记后会重新设置打断标记为false

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值