多线程(二):Thread 类及常见方法

Posted 头发都哪去了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程(二):Thread 类及常见方法相关的知识,希望对你有一定的参考价值。

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。

Thread 的常见构造方法

在这里插入图片描述
示例如下

public class ThreadDemo11 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("线程名:" +
                        Thread.currentThread().getName());
            }
        };

        Thread t1 = new Thread(runnable, "thread1");
        Thread t2 = new Thread(runnable, "thread2");
        Thread t3 = new Thread(runnable, "thread3");
        Thread t4 = new Thread(runnable, "thread4");
        Thread t5 = new Thread(runnable, "thread5");
        Thread t6 = new Thread(runnable, "thread6");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}

该代码执行结果如下
在这里插入图片描述

ThreadGroup:线程分组

可以将一类线程归为一组,并且进行信息的打印,查看一组线程的具体行为。

以张三和李四百米赛跑为例

/*
 * 演示线程分组
 */
public class ThreadDemo12 {
    public static void main(String[] args) {

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("开始起跑了~" +
                        Thread.currentThread().getName());
                try {
                    int num = 1 + new Random().nextInt(5);
                    Thread.sleep(num * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我到终点了~" +
                        Thread.currentThread().getName());
            }
        };
        //定义分组
        ThreadGroup group = new ThreadGroup("百米赛跑一组");
        //创建运动员
        Thread t1 = new Thread(group, runnable, "张三");
        Thread t2 = new Thread(group, runnable, "李四");
        t1.start();
        t2.start();

//        //打印线程分组的详情
//        group.list();
        
        //等待所有选手到达终点
        while (group.activeCount() != 0) {
        }

        //宣布成绩
        System.out.println("宣布成绩");

    }
}

该代码执行结果如下
在这里插入图片描述

Thread 的几个常见属性

在这里插入图片描述

  1. ID 是线程的唯一标识,不同线程不会重复
  2. 名称是各种调试工具用到
  3. 状态表示线程当前所处的一个情况,下面我们会进一步说明
  4. 优先级高的线程理论上来说更容易被调度到
  5. 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
  6. 是否存活,即简单的理解,为 run 方法是否运行结束了
  7. 线程的中断问题,下面我们进一步说明
public class ThreadDemo13 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        System.out.println("线程启动前状态:" + t1.getState());
        t1.start();
        System.out.println("线程启动后状态:" + t1.getState());
        System.out.println("线程ID:" + t1.getId());
        System.out.println("线程的名称:" + t1.getName());
        System.out.println("线程优先级:" + t1.getPriority());
        System.out.println("线程是否为后台线程:" + t1.isDaemon());
        System.out.println("线程是否存活:" + t1.isAlive());
        System.out.println("线程是否被中断:" + t1.isInterrupted());
    }
}

该代码执行结果如下
在这里插入图片描述

线程优先级

从上述代码可以观察到,新创建的线程优先级默认为5。线程优先级最小为1,最大为10。这个值越大那么它的(执行)权重就越高。

/*
 * 优先级演示
 */
public class ThreadDemo14 {
    public static void main(String[] args) {

        // 定义方法
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("线程名:" +
                        Thread.currentThread().getName());
            }
        };

        for (int i = 0; i < 10; i++) {
            Thread t1 = new Thread(runnable, "t1");
            Thread t2 = new Thread(runnable, "t2");
            Thread t3 = new Thread(runnable, "t3");
            t1.setPriority(1); // 优先级最小
//            t1.setPriority(Thread.MIN_PRIORITY); // 优先级最小
            t3.setPriority(Thread.MAX_PRIORITY); // 最高的优先级
            t1.start();
            t2.start();
            t3.start();
        }

    }
}

代码执行结果
在这里插入图片描述
可见:优先级越高,执行的优先级也越高,执行权也就越大,但是,CPU的调度是非常复杂的,所以不会严格按照优先级的排序去执行。总体来看,还是优先级越高,执行的权重也就越高。

线程分类

后台线程(守护线程)

后台线程(守护线程)用来服务用户线程。
使用场景:Java垃圾回收器
注意事项:

  1. 守护线程设置必须在调用start();前,如果设置守护线程再线程开始后,那么程序就会报错,并且设置的守护线程的值无法生效。
  2. 在守护线程里面创建的线程,默认情况下全部都是守护线程。

用户线程【默认为用户线程】

进程退出:没有用户线程运行时,进程就会结束。

public class ThreadDemo15 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            // 在守护线程的内部创建的线程
            Thread t2 = new Thread(() -> {
            });
            System.out.println("t2 是:" +
                    (t2.isDaemon() == true ? "守护线程" : "用户线程"));
        });
        // 设置为守护线程
//        t1.setDaemon(true);
        t1.start();

        System.out.println("t1 是否为守护线程:" + t1.isDaemon());
    }
}

该代码执行结果如下
在这里插入图片描述

start 和 run 的区别

先看两个例子
调用start方法时,代码如下:

public class ThreadDemo16 {
    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            System.out.println("线程名:" + Thread.currentThread().getName());
            System.out.println("当前时间 :" + new Date());
        }, "t1");

        t1.start();
        t1.start();
//        t1.run();
//        t1.run();
    }
}

该代码执行结果如下
在这里插入图片描述
调用run方法时,代码如下:

public class ThreadDemo16 {
    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            System.out.println("线程名:" + Thread.currentThread().getName());
            System.out.println("当前时间 :" + new Date());
        }, "t1");

//        t1.start();
//        t1.start();
        t1.run();
        t1.run();
    }
}

该代码执行结果如下:
在这里插入图片描述

start 和 run 的区别:

  1. run方法属于普通方法;而start方法属于启动线程的方法。
  2. run方法可以多次执行;而start方法只能执行一次,执行多次就会报错。

中断线程

线程中断方式有两种:
方式①:使用全局自定义变量来终止线程 。
这个终止的 方法比较温柔,在执行终止指令之后,需要执行完当前的任务才会真正停止。
示例如下:

/*
 * 使用全局自定义变量来终止线程
 */
public class ThreadDemo17 {

    //声明全局自定义变量
    private static boolean flag = false;

    public static void main(String[] args) throws InterruptedException {

        //转账线程
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!flag) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("转帐中~~");
                }
                System.out.println("转账终止~");
            }
        }, "转账");
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(310);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //改变变量的状态来终止线程执行
                System.out.println("停止交易~");
                flag = true;
            }
        });
        t2.start();

        t1.join();
        t2.join();
    }
}

该代码执行结果如下:
在这里插入图片描述

方式②: 使用线程提供的终止方法 interrupt 来终止线程
interrupt()方法的作用就是将线程中的终止状态从默认的 false 改为 true 。
示例如下:

/*
 *使用 interrupt 来终止线程
 */
public class ThreadDemo18 {
    public static void main(String[] args) throws InterruptedException {
        //创建转账线程
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.interrupted()) {
                    //判断线程中断状态
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
//                        e.printStackTrace();
                        break;
                    }
                    System.out.println("转帐中");
                }
                System.out.println("转账终止");
            }
        });
        t1.start();

        Thread.sleep(310);
        //终止线程
        System.out.println("停止交易");
        t1.interrupt();
        //中断线程
    }
}

代码执行结果如下:
在这里插入图片描述

此方式中真正终止线程的语句为try{ }中的break;语句,示例如下:

public class ThreadDemo19 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
//                while (!Thread.interrupted()) {
                    System.out.println("转账中");
                }
                System.out.println("转账终止");
            }
        });
        t1.start();

        TimeUnit.NANOSECONDS.sleep(1);
        System.out.println("停止交易");
        t1.interrupt();
    }
}

该代码执行结果如下:
在这里插入图片描述

其中,判断线程中断状态语句:while (!Thread.interrupted())的效果等价于while (!Thread.currentThread().isInterrupted()),二者的区别在于,使用while (!Thread.interrupted())时当终止完了一个线程之后,Thread.interrupted()的状态会复位,而使用while (!Thread.currentThread().isInterrupted())当终止完了一个线程之后,Thread.interrupted()的状态不会复位。

示例一:

public class ThreadDemo20 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.interrupted());
                }
            }
        });
        t1.start();
        
        t1.interrupt();

    }
}

使用Thread.interrupted()的运行结果为:
在这里插入图片描述
示例二:

public class ThreadDemo20 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
多线程的Thread 类及方法

Thread 类及常见方法

Java遨游在多线程的知识体系中

Java遨游在多线程的知识体系中

Java中Thread类及常见方法

Java中Thread类及常见方法