本文让我们一起来认识Thread的基本用法,包括如何创建线程,中断线程,线程等待,线程休眠,获取线程实例等方法

目录

观察并体会多线程代码执行

线程创建

线程中断

方案一:手动创建一个结束标志位

方案二:使用线程自带的标志位 

线程等待

线程休眠

获取线程实例


Java线程管理库 java线程代码实例_1024程序员节


观察并体会多线程代码执行

public class ThreadDemo4 {
    public static void main(String[] args) {
        //这里创建了一个t线程,线程代码是死循环打印 hello world
        Thread t = new Thread(()->{
            while(true){
                System.out.println("hello world");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //新线程入口
        t.start();
        //主线程死循环打印hello t
        while(true){
            System.out.println("hello t");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

上面代码,其中一个为我们执行代码的主线程(main线程),另一个则是我们自己创建的 t 线程 ,t 线程死循环打印 hello word ,主线程则死循环打印 hello world 。

在没学线程之前,我们按照普通调用方法来看,按理说不会打印 hello t ,因为 在进入t 线程里的代码已经死循环了,那么结果真的是这样吗?

下面给出结果

Java线程管理库 java线程代码实例_开发语言_02

此时是hello t 和 hello world 交替打印,为什么呢?

因为这里是两个线程,这两个线程是同时运行的,两个运行顺序并不相干。

当我们运行程序的时候 主线程在运行,当代码执行到start开始执行 t 线程时,此时两个线程同时执行。

此时我们就能很直观的观察到多线程的执行状态了

线程创建

创建线程主要是创建一个线程类(依靠Thread来创建),然后得到一个线程的实例对象,其中需要注意的是需要重写Thread中的特殊的方法(run方法),run方法可以看着新线程的main方法,由线程入口start方法来开启线程,此时会创建一个新线程并且执行

线程的创建大致有四种方法

1. 创建一个继承Thread的类,类中重写run方法,再进行实例化对象。

2.通过创建类实现Runnable接口,类中重写run方法,再进行实例化对象

3.通过Thread匿名内部类实现,类中重写run方法

4.通过lambda表达式实现

其中使用最广泛的是使用lambda表达式来创建一个基于Thread的匿名内部类的实例,代码如下

public class Test{
    public static void main(String[] args){
        //此处用lambda表达式创建 语法()->{}  {}里面是执行的run语句
        Thread t = new Thread(() -> {
        //此处写需要实现的代码,我们想要执行新线程的代码
            System.out.println("hello world");
        });    
        
        //调用start来执行新线程
        t.start();
    }
}

此处结果则是打印hello world,那么此时这里就有两个线程了,其中一个为我们执行代码的主线程(main线程),另一个则是我们自己创建的 t 线程 ,且此时打印的hello world 是 t 线程代码执行产生的结果

能在main方法里直接调用run方法吗?

可以调用,虽然此时结果仍然是打印 hello world

但是这里有一个很重要区分,如果我们直接调用run方法,而不是通过start方法(线程入口方法),那么此时就是一个普通的调用方法,打印的hello world 是主线程作用的结果,并不会创建一个新线程执行代码,

线程中断

有时我们创建的线程,线程在执行时,有时需要中断线程,让线程下来停下来,那么此时我们该怎么操作呢

方案一:手动创建一个结束标志位

public class Test{
    //此处手动设置一个结束标志位 ct
    public static boolean ct = false;
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while(!ct){
                System.out.println("hello world");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t.start();
        
        //把标志位置位true,上面的循环就结束了,线程也就执行完了
        ct = true;
    }
}

此时我们手动设置了一个 ct 的成员变量为结束标志位,直接给定为false(在lambda中进行变量捕获,后面有解释),其中我们 t 线程是在死循环的,当把标志位ct 置为true,此时 t 线程从循环中退出,线程结束。

需注意:lambda表达式中如果需要引用到外界的变量,那么需要实现变量捕获

变量捕获规则:Java规定变量捕获时,捕获的变量必须是final修饰的变量,或者是本质上的final变量(不被final修饰,但是没有更改过的变量)

方案二:使用线程自带的标志位 

通过isInterrupted方法获取,默认为false;

public class Test {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            //Thread.currentThread()获取当前进程的引用
            //通过自带的isInterrupted方法来获取标志位(默认为false)
           while(!Thread.currentThread().isInterrupted()){
               System.out.println("hello world");
               //情况二:终止的线程被堵塞(休眠等操作堵塞了线程)
               //try {
               //    Thread.sleep(1000);
               //} catch (InterruptedException e) {
               //    e.printStackTrace();
               //    break;//线程介绍的标志(得显式结束)
               //}
           }
        });

        t.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
           
        //通过interrupt方法把标志为置为true
        t.interrupt();
    }
}

第一种情况:

通过调用 isInterrupted方法来获取一个标志位(默认为false),此时 t 线程开始执行,那么线程代码是死循环的,那么我们通过interrupt方法来把标志为转换为true,此时线程就结束了。

第二种情况:

如果通过interrupt方法来结束线程,当要结束的线程正在执行sleep等会堵塞线程的代码时(休眠线程),此时终止休眠,sleep会抛出一个异常,并且把标记位重置(默认为false),那么此时通过interrup方法把标记位置为true后,sleep有把标志为重置为默认状态了,那么进程也并不会停止结束,只是会单独的抛出一个异常。

那么此时需要结束线程,则需要捕获捕获异常,然后显式结束(同break或者return等结束操作)

Java线程管理库 java线程代码实例_java_03

如果没有显式结束,那么此时线程通过sleep等会堵塞线程的方法来抛出异常,并且继续执行。

 

线程等待

方法:join();

例如在main线程里,执行 t.join();

那么此时,main线程等待 t 线程执行,意思就是 :(此时main线程不再执行,等 t 线程执行结束,然后main线程再继续执行)

join方法有两种形态

第一种:不设置参数,则会进行死等待,必须等到 t 线程执行结束,main才会继续执行 

第二种:join(参数),设置等待时长,单位毫秒,此时不会进行死等待,当等待时长到了,main线程不管 t 线程执没执行完都继续执行

t.join();
t.join(1000);

线程休眠

方法:sleep(参数)

通过Thread类来调用:Thread.sleep(参数)

线程执行到这句代码处,则该线程休眠,休眠时长根据参数而定,单位毫秒

注:调用sleep需要抛出或者捕获InterruptedException这个异常

Thread.sleep(1000);//此处线程休眠1000毫秒

获取线程实例

Thread Thread = Thread.currentThread();//获取当前线程的实例

通过Thread类调用static方法 currentThread()方法可以获取当前线程的实例,通过实例可以调用Thread类中的众多非静态的成员方法


本篇内容介绍到这里就差不多结束了,有其他知识点我们后面再作补充

Java线程管理库 java线程代码实例_Java线程管理库_04

Java线程管理库 java线程代码实例_java-ee_05