Thread 类的基本用法

目录

一.什么是Thread类

二.线程创建

2.1.继承Thread类

[2.2.实现 Runnable 接口](#2.2.实现 Runnable 接口)

[2.3.匿名内部类创建 Thread ⼦类对象](#2.3.匿名内部类创建 Thread ⼦类对象)

[2.4.匿名内部类创建 Runnable ⼦类对象](#2.4.匿名内部类创建 Runnable ⼦类对象)

[2.5.lambda 表达式创建 Runnable 子类对象(最常用,后续创建我都会用这种方法)](#2.5.lambda 表达式创建 Runnable 子类对象(最常用,后续创建我都会用这种方法))

三.线程中止

3.1.通过共享的标记来进行沟通

[3.2.调用interrupt() 方法来通知](#3.2.调用interrupt() 方法来通知)

三.线程等待

四.线程休眠

五.获取线程实例


一.什么是Thread类

java.lang.Thread 类是 Java 实现多线程编程的基础核心类,用于创建、控制和管理线程。运用时无需手动导包。

二.线程创建

start()方法--启动一个线程

作用:启动一个新线程,JVM自动调用run方法

注意:start()方法仅允许调用一次,多次调用会报异常

创建一个 Thread 对象,并不代表进程中就存在了对应的执行线程;只有调用 start() 方法,才会真正在进程中创建并启动线程。

2.1.继承Thread类

通过自定义一个类继承Thread类,并重写他的run()方法

java 复制代码
//继承 Thread, 重写 run
class myThread extends Thread{
    public void run(){
        while(true){
            System.out.println("my thread");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Demo1 {
    public static void main(String[] args) throws InterruptedException{
        Thread t=new myThread();
        t.start();
        while(true){
            System.out.println("my main");
            Thread.sleep(1000);
        }
    }
}

2.2.实现 Runnable 接口

可以看到Thread类本身实现了Runnable接口,同时其构造方法支持接收Runnable 接口的实现类。所以我们先定义一个类实现Runnable接口

并重写 run() 方法,再将该实现类的实例传入 Thread类的构造方法来创建线程。

java 复制代码
class myRunnable implements Runnable{
    public void run(){
        while(true){
            System.out.println("my thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Demo2 {
    public static void main(String[] args) throws InterruptedException{
        Runnable runnable=new myRunnable();
        Thread t=new Thread(runnable);
        t.start();
        while(true){
            System.out.println("main");
            Thread.sleep(1000);
        }
    }
}

2.3.匿名内部类创建 Thread ⼦类对象

java 复制代码
//继承 Thread, 重写 run, 使用匿名内部类
public class Demo3 {
    public static void main(String[] args) throws InterruptedException{
        Thread t=new Thread(){
            public void run(){
                while(true){
                    System.out.println("my thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t.start();
        while(true) {
            System.out.println("my main");
            Thread.sleep(1000);
        }
    }
}

2.4.匿名内部类创建 Runnable ⼦类对象

java 复制代码
//实现 Runnable, 重写 run, 使用匿名内部类
public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("my thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Thread t=new Thread(runnable);
        t.start();
        while(true){
            System.out.println("main");
            Thread.sleep(1000);
        }
    }
}

2.5.lambda 表达式创建 Runnable 子类对象(最常用,后续创建我都会用这种方法)

java 复制代码
//使用 lambda 表达式
public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while(true) {
                System.out.println("my thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        while(true){
            System.out.println("main");
            Thread.sleep(1000);
        }
    }
}

三.线程中止

3.1.通过共享的标记来进行沟通

首先我们来看一个写法

java 复制代码
public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        boolean isFinished=false;
        Thread t1=new Thread(()->
        {
            while(!isFinished){
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t1线程结束");
        });
     t1.start();
     //保证t1线程启动一段时间后
     Thread.sleep(3000);
     isFinished=true;
     System.out.println("main线程结束");
    }
}

这段看似正确的代码却报错了,那么问题出在哪,为什么会报错呢?

java 复制代码
Thread t1=new Thread(()->
        {
            while(!isFinished){
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t1线程结束");
        });

我们知道这是一个Lambda表达式,主线程先执行完创建线程的逻辑,未来由 JVM 线程调度器自动调用 这个任务里的代码, 这种先定义后被别人调用 的函数**,**就是回调函数。

我们看到这个Lambda内部使用了外部方法的变量,这个过程就叫变量捕获,Java对捕获变量是有强制要求的,他要求Lambda捕获的变量必须是**有效不可变的(被定义为final,或者一直不改变),**因为变量捕获是值捕获,捕获的是变量当前值,而不是变量本身,如果变量发生改变,不会影响到捕获的值,就会产生诡异的局面,两个同名变量但是值不同。

java 复制代码
public class Demo4 {
    static boolean isFinished=false;
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->
        {
            while(!isFinished){
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t1线程结束");
        });
     t1.start();
     //保证t1线程启动一段时间后
     Thread.sleep(3000);
     isFinished=true;
     System.out.println("main线程结束");
    }
}

如果我们将变量定义成一个全局的变量,每次拿到他都是拿的变量本身

为了避免自己创建的标记产生上面的问题,我们可以用到Java自带的现成变量。

3.2.调用interrupt() 方法来通知

我们知道Lambda定义是在创建现成之前,也就是在类对象引用声明之前,故而无法通过类对象的引用直接来调用isInterrupted()方法来判断线程是否被终止,所以我们需要借用Thread的另一个方法

我们无法看到这个方法是内部如何写的,由native修饰,底层为CPP代码实现,但是我们知道她的的用处就是:在哪个线程被调用,返回的就是哪个线程的Thread引用。返回true就是已经被标记为中断状态。

interrupt()的方法是主动进行终止

java 复制代码
public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->
        {
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("thread");
            }
            System.out.println("t1线程结束");
        });
        t1.start();
        //保证t1线程启动一段时间后
        Thread.sleep(1);
        t1.interrupt();
        System.out.println("main线程结束");
    }
}

可能有人会疑惑,诶?为什么这个创建的新线程不加上sleep方法了,这就是我要讲的interrupt()方法的另外一个作用,唤醒像sleep()这样的阻塞方法(强制唤醒),强制唤醒就会导致异常。

java 复制代码
public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->
        {
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                   throw new RuntimeException();
                }
            }
            System.out.println("t1线程结束");
        });
        t1.start();
        //保证t1线程启动一段时间后
        Thread.sleep(3000);
        t1.interrupt();
        System.out.println("main线程结束");
    }
}

当前线程直接终止,后续线程代码不会执行

java 复制代码
public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->
        {
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                   break;
                }
            }
            System.out.println("t1线程结束");
        });
        t1.start();
        //保证t1线程启动一段时间后
        Thread.sleep(3000);
        t1.interrupt();
        System.out.println("main线程结束");
    }
}

但是如果我们在sleep()强行唤醒后捕捉到异常后break终止循环,那么还是可以继续执行捕捉到异常之后的代码,但是如果我们在捕获异常中什么都不做,又会产生什么现象呢

java 复制代码
public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->
        {
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //throw new RuntimeException();
                    //break;
                }
            }
            System.out.println("t1线程结束");
        });
        t1.start();
        //保证t1线程启动一段时间后
        Thread.sleep(3000);
        t1.interrupt();
        System.out.println("main线程结束");
    }
}

按理说,我们调用了interrupt()方法,线程已经终止了,为什么运行的结果和我们所想是完全不同的呢?这是因为sleep()方法被强制唤醒后,又会把isinterrupted()方法内部的已经修改成true的标志位,又重新修改成false,所以循环还是会继续进行。

三.线程等待

有时,我们需要等待一个线程完成后,才进行自己的下一步工作

java 复制代码
public class Demo3 {
    static int count=0;
    public static void main(String[] args) {
        Thread t1=new Thread(()->
        {
            for (int i = 0; i < 100; i++) {
                count++;
            }
        });
        t1.start();
        System.out.println("count="+count);
    }
}

可以看到这个代码的运行结果是count=0,为什么呢?这是因为主线程提前运行结束了,可能t1线程还没启动,主线程就已经运行了打印count大小的代码,所以为了防止这种情况发生,我们需要让主线程等待t1线程运行完毕后再执行打印count变量的代码,于是join()方法就可以运用在这里

java 复制代码
package Thread;

public class Demo3 {
    static int count=0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->
        {
            for (int i = 0; i < 100; i++) {
                count++;
            }
        });
        t1.start();
        t1.join();
        System.out.println("count="+count);
    }
}

当然join()方法的括号中还可以加上参数,意思是等这么就,要是超过了,就不再等待

四.线程休眠

线程休眠的方法有两种:

  1. sleep(long millis) 休眠当前的线程,休眠的时间单位是毫秒
  2. sleep(long millis,int nanos) 休眠当前的线程,休眠单位是毫秒和纳秒

可能会被强制唤醒,所以要提前抛异常,或者用try...cache...捕获异常

代码调用sleep()方法,相当于当前线程让出CPU资源,后续时间到了的时候,需要操作系统内核把这个线程重新调用回CPU上,才能继续被执行,时间到了之后,意味着允许被调用,而不是立即执行,sleep(0)意味着线程自动放弃CPU资源,等待操作系统的重新调度。

五.获取线程实例

我们在'调用interrupt() 方法来通知',这一节已经详细解释过了,就是调用Thread类的静态方法currentThread()方法来获取当前线程的实例。

Thread 类的基本用法我们差不多讲解完毕了,想知道线程的更多知识,请听下回分解,下回我们会讲解线程的几种状态

相关推荐
machnerrn2 小时前
matlab实现直流伺服电机 PID 控制系统仿真系统(含源码+资料报告+说明文档等)
开发语言·matlab
Hello--_--World2 小时前
JS:this指向、bind、call、apply、知识点与相关面试题
开发语言·javascript·ecmascript
好家伙VCC2 小时前
**基于RISC-V架构的嵌入式系统开发:从零开始构建高效低功耗应用**在当前物联网(IoT)和边缘计
java·python·物联网·架构·risc-v
wyu729612 小时前
Spring框架学习笔记:从IoC到声明式事务
java
沐知全栈开发2 小时前
CSS Text(文本)
开发语言
前进吧-程序员2 小时前
现代 C++ 异步编程:从零实现一个高性能 ThreadPool (C++20 深度实践)
开发语言·c++·c++20
qqacj2 小时前
Spring Security 官网文档学习
java·学习·spring
Rsun045512 小时前
10、Java 桥接模式从入门到实战
java·开发语言·桥接模式
金銀銅鐵2 小时前
[Java] 从 class 文件看 cglib 对 MethodInterceptor 的处理 (下)
java·后端