java Thread常用方法解析

Constructor

public Thread()
public Thread(Runnable target)
public Thread(ThreadGroup group, Runnable target)
public Thread(String name)
public Thread(ThreadGroup group, String name)
public Thread(Runnable target, String name)
public Thread(ThreadGroup group, Runnable target,String name)

以上都是Thread的构造函数,可以看到我们可以传入一个Runnable的任务,也可以在传入Runnable任务的同时也可以指定线程组,或者直接指定线程的名字。
这些重载方法根据自己的需要和环境来传参。

public static void sleep(long millis)throws InterruptedException

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权,这句话是根据锁来理解的,也就是说调用了sleep方法,线程进入休眠,但是仍然保持对同步监视器的锁定状态,在该期间,其他线程无法继续加锁。

public static void sleep(long millis, int nanos) throws InterruptedException

在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),其实是对休眠的时间做了更精确的规定,精度到纳秒,第二个参数就是纳秒,这个方法知道就行。

public final void setPriority(int newPriority)

设置线程的优先级,线程的优先级是一个整数,在1-10之间,数字越大,优先级越高,Thread中有一个属性priority来保存优先级,Thread中有三个静态常量
MAX_PRIORITY :10
MIN_PRIORITY : 1
NORM_PRIORITY: 5
需要注意的是,java提供了10个优先级别,但是操作系统并不是10个,有时候可能不能很好的对应,所以在没有特定需求的时候,尽量使用上面的三个静态常量,以保证java良好的可移植性。

public final int getPriority()

返回线程的优先级,返回一个int值

public static void yield()

暂停当前正在执行的线程对象,并执行其他优先级不小于该线程的线程。这个过程仅仅是暂停该线程,然后系统的线程调度器重新调度一次,优先级不小于该线程的线程开始执行,但是还有可能出现暂停后线程调度器再次调度该线程,这种情况说明其他线程的优先级都比该线程小。

public void start()

使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
结果是两个线程并发地运行:当前线程(调用该方法的线程)和另一个线程(执行其 run 方法的新线程)。多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。

public void run()

run()方法又叫做线程体,是线程中最主要的部分,如果创建线程的时候不实现或者重写这个方法,那么线程就没有意义了。如果直接调用run()方法,那么就相当于调用了一个普通函数。

下来我们看看run()和start()有什么区别
1.start()方法是用来启动一个线程,但是并不是仅仅的调用run()方法,而是通过线程调度器同时启动多个线程(如果有多个线程)的run方法,是真正的分线程在运行run()方法
2.run()方法直接调用就相当于调用了一个普通方法,这个时候并没有线程启动,只是在主线程中去调用普通run方法,这时就只有一个主线程,没有其他线程。我们来看一个demo

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + i);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "testThread1");
        thread1.start();
        thread1.run();
    }

首先解释一下demo,我用Lambda表达式创建了线程thread1,实现run方法中,我打印线程名加上循环变量i,每次休眠2秒,并且直接通过构造函数给线程命名为testThread1
下面我先调用start方法,然后调用run方法
通过上面的分析,我们可以知道调用run方法并不会创建线程,所以run()跑出来的结果打印的线程名肯定是main
看看运行结果
在这里插入图片描述
运行结果和我们分析的一样,当我们调用start方法时,另起了一个线程testThread,和main线程并列,然后我们又在main线程中调用了普通方法run(),打印的结果是交叉的,说明两个线程同时在运行,这样就证实上面的分析,调用start方法会同时执行多个线程的run方法。

public final void join() throws InterruptedException

暂停外层线程,让调用该方法的线程执行完后再执行外层线程
看一个demo:
A线程中有B线程,B线程中有C线程,要求先让C线程执行,再执行B,最后执行A

    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            Thread threadB = new Thread(() -> {
                Thread threadC = new Thread(() -> {
                    System.out.println("Thread C");
                });
                threadC.setName("C");
                threadC.start();
                try {
                    threadC.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread B");
            });
            threadB.setName("B");
            threadB.start();
            try {
                threadB.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread A");
        });
        threadA.setName("A");
        threadA.start();
    }

代码解释:直接通过Lambda表达式传给Thread,new出嵌套关系的A,B和C线程。嵌套关系中调用join方法,即在B线程中C调用join方法,然后再执行B的内容,在A中B调用join方法,再执行A的内容,这样就可以达到要求了。
在这里插入图片描述

public final void join(long millis) throws InterruptedException
public final void join(long millis, int nanos) throws InterruptedException
这两个方法不过是join方法规定了等待的时间,如果过了等待的时间,则该线程不再等待

public final void setDaemon(boolean on)

设置线程是/不是守护线程,传参true为设置为守护线程,传参false为设置为非守护线程
守护线程:又叫做后台线程和精灵线程,是用来为其他线程提供服务的线程,gc垃圾回收线程就是一个后台线程。
需要注意的是,前台线程全部死亡时,后台线程也跟着死亡,jvm就会退出。这个不难理解,后台线程总归还是要为前台线程提供服务,如果前台线程都已经全部死亡了,后台线程就没有存在的必要了,当没有线程的时候,jvm也就退出。
我们来看一个demo

    public static void main(String[] args) {
         // 创建子线程
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println(Thread.currentThread().getName() + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //设置该线程为守护线程
        thread.setDaemon(true);
        thread.start();
        //主线程内跑5个数字
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

demo解释:我们创建一个每隔1秒打印循环变量的子线程thread,然后让他循环1000次,然后我们调用setDaemon将该子线程设置为守护线程,在主线程中打印5次循环变量,这时我们来看看结果
在这里插入图片描述
子线程并没有打印到1000,而是随着主线程打印完5个就死亡了。所以,前台线程全部死亡时,后台线程也会跟着死亡,jvm退出。
注意:设置守护线程时,需要放在start前执行,否则会抛异常。

public void interrupt()

public void interrupted()

public void isInterrupted()

这三个方法都是用来中断线程,interrupt方法用于中断线程,该方法只是一个标记方法,并不会真正意义上中断线程,真正实现中断逻辑需要我们自己实现。
isInterrupted方法是用来判断线程是否中断,也就是检测interrupt方法所修改的标记,这个方法和interrupt方法是联合起来用的。
interrupted方法也是用来判断线程是否中断,功能和isInterrupted方法相似,但是不同的是interrtupted方法在判断完后会清楚中断标记。
通过一个demo来看一下:

public class TestThread {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println("running");
            }
        });
        thread.start();
        thread.interrupt();
    }
}

可以看到在主线程中中断了thread线程,但是结果却不会停下来。刚才也说过了,interrupt()方法是一个标记方法,他只会去改变中断标记,并不会真正的中断线程,具体的中断逻辑需要自己实现,我们配套判断中断方法isInterrupted实现,修改一下代码即可

public class TestThread {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true) {
            	//实现中断逻辑
                if(Thread.currentThread().isInterrupted()) {
                    break;
                }
                System.out.println("running");
            }
        });
        thread.start();
        //先让thread线程跑500毫秒
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}

通过自己的实现逻辑,可以很好的中断线程了,为什么说很好呢,因为Java提供了一个stop方法来停止线程,当调用这个方法的时候,会立即停止线程,不管线程的任务进行到什么地步,这样做很突兀,也会造成很多错误。

猜你喜欢

转载自blog.csdn.net/weixin_42220532/article/details/88926720