java干货 线程的常用函数以及生命周期

一、常用函数

1.静态方法

1.1 sleep方法:让线程由执行状态进入休眠状态,休眠时间可以指定,单位毫秒,在休眠时间内,线程不会获得cpu执行的机会,也就是说该线程内存空间内的代码不会被执行,等到过了休眠时间,进入就绪状态,获取到cpu的执行时间

源码1,C++本地方法:
public static native void sleep(long millis) throws InterruptedException;
源码2,参数1,休眠时间,参数2,额外睡眠时间,单位纳秒,本质还是调用 native 方法
public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0 && millis < Long.MAX_VALUE) {
            millis++;
        }

        sleep(millis);
    }
使用:
Thread.sleep(2*1000); //休眠两秒
Thread.sleep(2*1000,5); //休眠两秒,额外休眠5纳秒

1.2 yield 方法:yield意为让步,由运行状态回到就绪状态,可能进入就绪状态后又被执行。yied让出cpu占有权,让出的时间是不可设定的,cpu可能会忽略这一个提示,这跟设置线程优先级一样,只是起到提示的作用。

源码,是一个C++ 本地方法:
public static native void yield();
class Task1 implements Runnable{
    @Override
    public void run(){
        for(int i =0;i<5;i++){
            System.out.println("Task1: " + i);
        }
    }
}

class Task2 implements Runnable{
    @Override
    public void run(){
        Thread.yield();
        for(int i =0;i<5;i++){
            System.out.println("--->Task2: " + i);
        }
    }
}

class Task3 implements Runnable{
    @Override
    public void run(){

        for(int i =0;i<5;i++){
            System.out.println("Task3: " + i);
        }
    }
}
public class Demo3 {
    public static void main(String[] args) {
        Task1 task1 = new Task1();
        Thread thread1 = new Thread(task1);
        thread1.setPriority(Thread.MAX_PRIORITY); // 线程1 设置最大优先级

        Task2 task2 = new Task2();
        Thread thread2 = new Thread(task2);
        thread2.setPriority(Thread.MIN_PRIORITY); // 线程2 设置最小优先级

        Task3 task3  = new Task3();
        Thread thread3 = new Thread(task3);
        thread3.setPriority(Thread.MAX_PRIORITY); // 线程3 设置最大优先级


        thread1.start();
        thread2.start();
        thread3.start();
    }
}
运行结果:
Task1: 0
--->Task2: 0
--->Task2: 1
Task3: 0
--->Task2: 2
Task1: 1
--->Task2: 3
--->Task2: 4
Task3: 1
Task1: 2
Task3: 2
Task3: 3
Task1: 3
Task3: 4
Task1: 4

分析:虽然线程2 调用yield方法,线程1和线程3有最高优先级,线程2有最低优先级,但是线程2仍然有cpu执行时间,说明yield只是起到对调度器的提示作用。

1.3.currentThread方法: 获取当前线程

源码:
public static native Thread currentThread();
使用:
Thread thead = Thread.currentThread();

2.实例方法

2.1 start:创建并启动线程,本质是C++ 创建线程,调用的是本地方法

源码:
 private native void start0(); 
public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

2.2 run 方法:调用start 方法才会真正创建线程,直接调用run方法并不会创建一个新的线程,直接调用相当于一个调用一个普通方法

 源码:
 @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

2.3 获取和设置线程名称

 源码:
 public final String getName() {
        return name;
    }
 public final synchronized void setName(String name) {
        checkAccess();
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        if (threadStatus != 0) {
            setNativeName(name);
        }
    }

2.4 设置和获取线程优先级,最大优先级10,最小优先级1,默认优先级5

源码:
    public static final int MIN_PRIORITY = 1;

    public static final int NORM_PRIORITY = 5;

    public static final int MAX_PRIORITY = 10;
    
 public final int getPriority() {
        return priority;
    }
    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

2.5 获取线程是否中断, isInterrupted() 不会重置状态为false,是一个实例方法

源码:
public boolean isInterrupted() {
        return interrupted;
    }

对比静态方法 interrupted会将中断状态设置为false

源码:
public static boolean interrupted() {
        Thread t = currentThread();
        boolean interrupted = t.interrupted;
        // We may have been interrupted the moment after we read the field,
        // so only clear the field if we saw that it was set and will return
        // true; otherwise we could lose an interrupt.
        if (interrupted) {
            t.interrupted = false;
            clearInterruptEvent();
        }
        return interrupted;
    }

2.6 中断线程,哪个线程调用该方法,哪个线程就被中断

源码:
public void interrupt() {
        if (this != Thread.currentThread()) {
            checkAccess();

            // thread may be blocked in an I/O operation
            synchronized (blockerLock) {
                Interruptible b = blocker;
                if (b != null) {
                    interrupted = true;
                    interrupt0();  // inform VM of interrupt
                    b.interrupt(this);
                    return;
                }
            }
        }
        interrupted = true;
        // inform VM of interrupt
        interrupt0();
    }

2.7 设置守护线程和判断是否是守护线程

源码:
public final boolean isDaemon() {
        return daemon;
    }
 public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }
  • 守护线程使用
    守护线程的特点:
    后台运行:守护线程在后台运行,不会阻止 JVM 退出。
    自动退出:当所有的非守护线程结束时,JVM 会自动退出,即使守护线程还在运行。
    不能依赖守护线程进行重要任务:守护线程在 JVM 退出时可能会突然终止,因此不应依赖它们来执行需要确保完成的重要任务。
    必须在调用 start() 方法之前调用 setDaemon(true) 方法,否则会抛出 IllegalThreadStateException。
class DaemonThread extends Thread{
    @Override
    public void run(){
        for (int i = 0;i < 8;i++){
            System.out.println("守护线程:" + i);
        }
        System.out.println("守护线程执行结束");
    }
}
class FrontThread extends Thread{
    @Override
    public void run(){
        for (int i = 0;i < 3;i++){
            System.out.println("--》前台线程:" + i);
        }
        System.out.println("======前台线程执行完成====!");
    }
}
public class Demo06 {
    public static void main(String[] args) {
        System.out.println("--------主线程开始!---------");
        DaemonThread daemonThread = new DaemonThread();
        daemonThread.setDaemon(true);  // 设置为后台线程
        daemonThread.start();

        FrontThread frontThread = new FrontThread(); // 前台线程
        frontThread.start();
        System.out.println("----主线程结束!----");
    }
}

输出结果:

--------主线程开始!---------
守护线程:0
守护线程:1
守护线程:2
守护线程:3
守护线程:4
----主线程结束!----
守护线程:5
守护线程:6
守护线程:7
守护线程执行结束
--》前台线程:0
--》前台线程:1
--》前台线程:2
======前台线程执行完成====!

分析:主线程和前台线程已经结束,jvm退出,导致守护线程未完成任务。

2.8 判断线程是否还存活

源码:
 public final native boolean isAlive();

2.9 join 方法: 主要功能是使调用线程等待,直到目标线程完成。这对于确保某些任务在目标线程完成之前不会继续执行非常有用。它是实现线程之间协调和同步的一个关键工具。
例如,在一个多线程程序中,主线程可能希望等待所有工作线程完成后再继续执行,这时候 join 方法就显得非常有用。

  • 举例:有线程t1t2以及主线程main,在main中创建并启动了t1和t2,并调用t1和t2的join方法(t1.join()、t2.join()),那么主线程在等待t1和t2执行一段时间后才会继续执行(哪个线程执行完了,就继续从调用join的地方继续执行),若过了指定时间t1和t2还未执行完,主线程还是会继续执行。
  • 注意 join 在start方法调用之后执行才生效
  • 源码:底层调用的使本地方法 wait()
public final native void wait(long timeoutMillis) throws InterruptedException;
public final void join() throws InterruptedException {
        join(0);
    }
public final synchronized void join(final long millis)
    throws InterruptedException {
        if (millis > 0) {
            if (isAlive()) {
                final long startTime = System.nanoTime();
                long delay = millis;
                do {
                    wait(delay);
                } while (isAlive() && (delay = millis -
                        TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
            }
        } else if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            throw new IllegalArgumentException("timeout value is negative");
        }
    }
public final synchronized void join(long millis, int nanos)
    throws InterruptedException {

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0 && millis < Long.MAX_VALUE) {
            millis++;
        }

        join(millis);
    }
  • 案例一:不使用join方法
class T1 implements Runnable{
    @Override
    public void run(){
        for(int i = 0;i < 10;i++){
            System.out.println("T1 :" + i);
        }
    }
}
class T2 implements Runnable{
    @Override
    public void run(){
        for(int i = 0;i < 10;i++){
            System.out.println("-->T2 :" + i);
        }
    }
}
public class Demo4 {
    public static void main(String[] args) {
        System.out.println("-------主线程开始执行---------");
        T1 t1 = new T1();
        Thread thread1 = new Thread(t1);

        T2 t2 = new T2();
        Thread thread2 = new Thread(t2);

        thread1.start();
        thread2.start();
        System.out.println("T1 和 T2 线程执行结束!继续主线程!");
    }
}
-------主线程开始执行---------
T1 和 T2 线程执行结束!继续主线程!
T1 :0
-->T2 :0
-->T2 :1
-->T2 :2
-->T2 :3
-->T2 :4
T1 :1
T1 :2
T1 :3
T1 :4
T1 :5
T1 :6
-->T2 :5
-->T2 :6
T1 :7
T1 :8
-->T2 :7
-->T2 :8
T1 :9
-->T2 :9

分析 每个线程都有cpu执行的机会,正常上下文切换

  • 案例二:使用join方法,不指定等待时间,这时主线程会等待 t1和t2 两个线程全部执行完,才会继续主线程执行
class T1 implements Runnable{
    @Override
    public void run(){
        for(int i = 0;i < 10;i++){
            System.out.println("T1 :" + i);
        }
    }
}
class T2 implements Runnable{
    @Override
    public void run(){
        for(int i = 0;i < 10;i++){
            System.out.println("-->T2 :" + i);
        }
    }
}
public class Demo4 {
    public static void main(String[] args) {
        System.out.println("-------主线程开始执行---------");
        T1 t1 = new T1();
        Thread thread1 = new Thread(t1);

        T2 t2 = new T2();
        Thread thread2 = new Thread(t2);

        thread1.start();
        thread2.start();
        try {
         // 等待t1和t2全部完成
            thread1.join(); 
            thread2.join();
        }catch (Exception e){

        }

        System.out.println("T1 和 T2 线程执行结束!继续主线程!");
    }
}

运行结果:

-------主线程开始执行---------
T1 :0
-->T2 :0
T1 :1
-->T2 :1
T1 :2
-->T2 :2
T1 :3
-->T2 :3
T1 :4
-->T2 :4
T1 :5
-->T2 :5
-->T2 :6
-->T2 :7
T1 :6
-->T2 :8
T1 :7
-->T2 :9
T1 :8
T1 :9
T1 和 T2 线程执行结束!继续主线程!

分析:在t1 和 t2全部执行完才继续执行主线程,等效于join(0)

  • 案例三 使用join 方法,并指定等待时间
class T1 implements Runnable{
    @Override
    public void run(){

        for(int i = 0;i < 10;i++){
            System.out.println("T1 :" + i);
        }
        try {
            Thread.sleep(1*1000); // 模拟处理时间过长 1 秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("T1 执行结束");
    }
}
class T2 implements Runnable{
    @Override
    public void run(){

        for(int i = 0;i < 10;i++){
            System.out.println("-->T2 :" + i);
        }
        try {
            Thread.sleep(50); // 模拟处理时间过长 50 毫秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("T2 执行结束");
    }
}
public class Demo4 {
    public static void main(String[] args) {
        System.out.println("-------主线程开始执行---------");
        T1 t1 = new T1();
        Thread thread1 = new Thread(t1);

        T2 t2 = new T2();
        Thread thread2 = new Thread(t2);


        thread1.start();
        thread2.start();
        try {
            thread2.join(10,1); // 等待 800 毫秒 1 纳秒
            System.out.println("-------等了t2 10 毫秒后 主线程继续执行---------");
            thread1.join(800); //等待 800 毫秒
        }catch (Exception e){

        }
        System.out.println("等了t1 800 毫秒 继续主线程!");
    }
}

-------主线程开始执行---------
T1 :0
-->T2 :0
T1 :1
-->T2 :1
T1 :2
-->T2 :2
T1 :3
-->T2 :3
T1 :4
T1 :5
-->T2 :4
T1 :6
-->T2 :5
T1 :7
T1 :8
-->T2 :6
-->T2 :7
-->T2 :8
-->T2 :9
T1 :9
-------等了t2 10 毫秒后 主线程继续执行---------
T2 执行结束
等了800 毫秒 继续主线程!
T1 执行结束

分析: 线程t1处理逻辑需要 1秒,t2处理任务需要 50 毫秒,等待t1 800毫秒,等待t2 10 毫秒,我们发现虽然 t2 没有在等待时间内完成而是超时了,主线程部分逻辑还是在t2执行完之前执行,而t1线程超时了,所以主线程在等了800毫秒后就执行了,此时t1还未执行完。

  • 案例四:join(0)和join()一样,表示等某线程处理完,回到调用join的地方继续执行
class T1 implements Runnable{
    @Override
    public void run(){
        for(int i = 0;i < 2;i++){
            System.out.println("T1 :" + i);
        }
    }
}
class T2 implements Runnable{
    @Override
    public void run(){

        for(int i = 0;i < 10;i++){
            System.out.println("-->T2 :" + i);
        }
    }
}
public class Demo4 {
    public static void main(String[] args) {
        System.out.println("-------主线程开始执行---------");
        T1 t1 = new T1();
        Thread thread1 = new Thread(t1);

        T2 t2 = new T2();
        Thread thread2 = new Thread(t2);


        thread1.start();
        thread2.start();
        try {
            thread1.join(0);
            System.out.println("-------等了t1 执行完 主线程继续执行---------");
            thread2.join(0);
        }catch (Exception e){

        }
        System.out.println("等了t2 执行完 继续主线程!");
    }
}

-------主线程开始执行---------
T1 :0
-->T2 :0
-->T2 :1
-->T2 :2
T1 :1
-->T2 :3
-------等了t1 执行完 主线程继续执行---------
-->T2 :4
-->T2 :5
-->T2 :6
-->T2 :7
-->T2 :8
-->T2 :9
等了t2 执行完 继续主线程!

二、生命周期

  • 从操作系统层面上,任何线程一般都具有五种状态,即创建、就绪、运行、阻塞、终止。
  • 从java 的getState()的返回值可以知道,有以下六种状态
    • NEW
      调用 new 线程后,进入NEW 状态
    • RUNNABLE
      调用start 后进入 RUNNABLE 状态
      获取到锁后 会由BLOCKED 进入 RUNNABLE 状态
    • TIMED_WAITING
      调用sleep(毫秒)、join(非零毫秒)、wait(非零毫秒)进入TIMED_WAITING 状态
    • WAITING
      join()、join(0)、join(0,0)、wait()、wait(0)、wait(0,0) 会进入 WAITING 状态
    • BLOCKED
      线程试图进入一个同步块或同步方法,但该对象的监视器锁已被其他线程持有
    • TERMINATED
      线程异常结束或正常执行完成后,进入 TERMINATED 状态
      图1
  • wait 会释放锁,sleep 不会释放锁
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值