WangJiQing

导航

线程常用方法

线程常用方法介绍

1、方法概述

方法说明 static 功能说明 注意
start() 启动一个新线程,
在新的线程运行
run方法中的代码
start方法只是让线程进入就绪,里面代码不一定立刻运行
(CPU的时间片还没分给它)。每个线程对象的start方法只
能调用一次,如果调用了多次会出现
IllegalThreadStateException
run() 新线程启动后会调
用的方法
如果在构造Thread对象时传递了Runnable参数,则线程启
动后会调用Runnable中的run方法,否则默认不执行任何操
作。但可以创建Thread的子类对象,来覆盖默认行为
join() 等待线程运行结束
join(long n) 等待线程运行结束,
最多等待n毫秒
getId() 获取线程id id唯一
getName() 获取线程名
setName(String) 修改线程名
getPriority() 获取线程优先级
setPriority(int) 修改线程优先 java中规定线程优先级是1~10的整数,默认是5,较大的优先级
能提高该线程被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、start() vc run()

start方法会启动一个线程,并执行实现的run方法,多次执行会抛异常:IllegalThreadStateException

单独调用run方法,会当作main线程中的一个普通线程

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            log.debug("hello world");
        }, "t");

        t.run();
        t.start();
        t.start();//start()方法多次启动会抛异常IllegalThreadStateException
    }

3、sleep vs yield

1、sleep

1.调用sleep会让当前线程从Runnable进入Timed_Waiting状态

2.其它线程可以使用interrupt方法打断正在睡眠的线程,这时sleep方法会抛出InterruptedException

3.睡眠结束后的线程未必会立刻得到快行

4.建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性

2、yield

1.调用yield会上当前线程从Running进入Runnable状态,然后调度执行其它同优先级的线程。如果这时没
有同优先级的线程,那么不能保证让当前线程暂停的效果
2.具体的实现依赖于操作系统的任务调度器

3、线程优先级

  • 线程优先级会提示(hit)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它

  • 如果CPU比较忙,那么优先级高的线程会获得更多的时间片,但CPU闲时,优先级几乎没作用

  • 优先级1-10,默认为5

4、示例

打断正在sleep、wait、join的线程,清楚打断标记,并抛出异常

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            try {
                log.debug("run...");
                TimeUnit.SECONDS.sleep(1);//还是使用了Thread.sleep(),线程执行1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t");
        t.start();

        log.debug("state1:{}", t.getState());
        try {
            Thread.sleep(500);//线程执行0.5秒,不停顿,容易导致t线程还没开始执行执行下面的语句:t.getState()等等,结果会不准确
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("state2:{}", t.getState());

        //打断正在sleep、wait、join的线程,清楚打断标记
        log.debug("interrupt...");
        t.interrupt();
        log.debug("打断标记:{}", t.isInterrupted());
    }

yield会让出CPU,测试优先级和yield

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            int count = 0;
            while (true) {
                Thread.yield();
                System.out.println("t1 ---->" + count++);
            }
        });

        Thread t2 = new Thread(() -> {
            int count = 0;
            while (true) {
                System.out.println("        t2 ---->" + count++);
            }
        });

//        t1.setPriority(Thread.MIN_PRIORITY);//优先级1
//        t2.setPriority(Thread.MAX_PRIORITY);//优先级10
        t1.start();
        t2.start();
    }

使用yield

使用优先级(注释 Thread.yield()😉,设置t1、t2的优先级

5、sleep防止CPU占用100%

CPU浪费(一核CPU的Linux)

public class Methods {
    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                //try {Thread.sleep(1);} catch (InterruptedException e) {}
            }
        }).start();
    }
}

去掉注释,使用sleep避免while(true)空转浪费CPU

  • 可以用wt或条件变量达到类似的效果
  • 不同的是,后两种都需要加锁,并且需要相应的唤醒操作,一般适用于要进行同步的场景
  • sleep适用于无需锁同步的场景

4、join

join,相当于插队,必须等该线程执行完,当前线程才能继续向下执行

示例,main线程需要t线程来计算结果,如果不等t线程计算完,就输出结果,会导致结果不正确

    static int result = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                log.debug("开始计算...");

                Thread.sleep(1000);
                result = 10;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("计算完成...");
        }, "t");

        t.start();

        //t.join();
        log.debug("结果:{}", result);
    }

加入t.join()

使用join(long millis),超时了不会抛出异常

5、interrupt

1、打断阻塞线程

打断阻塞线程,会清楚打断标记(标记线程是否被打断过)

public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(() -> {
        log.debug("hi");
        try {
            TimeUnit.MILLISECONDS.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "t");
    t.start();

    TimeUnit.MILLISECONDS.sleep(500);//相当于Thread.sleep(long millis);
    log.debug("开始打断...");
    t.interrupt();
    log.debug("打断标记  {}", t.isInterrupted());
}

2、打断运行

打断运行中的线程,打断标记为true

public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(() -> {
        while (true) {
            boolean interrupted = Thread.currentThread().isInterrupted();
            if (interrupted) {
                log.debug("打断运行,推出循环");
                break;
            }
        }
    }, "t");
    t.start();

    log.debug("开始打断...");
    t.interrupt();
    log.debug("打断标记  {}", t.isInterrupted());
}

6、两阶段终止模式

Two Phase Termination
在一个线程t1中如何优雅终止线程t2?这里的【优推】指的是给t2一个料理后事的机会。

1.错误思路

  • 使用线程对象的stop()方法停止线程,

    stop方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁

  • 使用System.exit(int)方法停I止线程

    目的仅是停止一个线程,但这种做法会让整个程序都停止

2、思路

graph TD a("while(true)") ---> b("有没有被打断") b --有--> c("料理后事") c ---> d((结束循环)) b --否--> e(睡眠2秒"") e -- 无异常 --> f("执行监控记录") e -- 有异常 --> g("设置打断标记") f ---> a g ---> a

3、示例

如果被打断线程正在sleep,wait,join会导致被打断的线程抛出InterruptedException,并清除打断标记;如果打断的正在运行的线程,则会设置打断标记;park的线程被打断,也会设置打断标记

public static void main(String[] args) throws InterruptedException {
    TwoPhaseTermination t = new TwoPhaseTermination();
    t.start();

    Thread.sleep(3500);
    t.stop();
}

static class TwoPhaseTermination{
    private Thread monitor;

    //启动监控现场
    public void start(){
        monitor = new Thread(() -> {
            while (true){
                boolean interrupted = Thread.currentThread().isInterrupted();
                if(interrupted){
                    log.debug("线程终止,料理后事");
                    break;
                }

                try {
                    log.debug("监控记录");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //重新设置打断标记,因为打断的是sleep中的线程,会清楚打断标记
                    //没有该语句打断后会继续打印 监控记录
                    monitor.interrupt();
                    e.printStackTrace();
                }
            }
        }, "monitor");

        monitor.start();
    }

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

posted on 2022-12-17 17:51  如梦幻泡影  阅读(24)  评论(0编辑  收藏  举报