JavaEE——Thread类的基本用法

目录

前言

上篇文章简单介绍了进程和线程,这边文章来简述一下Java中Thread类的基本用法,在Java中使用多线程编程。

1.线程创建

1.1 继承Thread类

我们创建一个继承自Thread的内部类,同时重写Thread的run()方法这里重写run()方法是用于编写我们想要此线程所要执行的功能的代码。即线程的入口

java 复制代码
//继承 Thread, 重写 run
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("线程运行");
    }
}

而后创建示例并且启动线程

java 复制代码
public class Demo1 {
    public static void main(String[] args) {
        //创建线程对象
        MyThread t = new MyThread();
        //启动线程
        t.start();
    }
}

注意,这里不能使用t.run()这是由于t.run()是用于执行线程,并不能创建一个新线程,虽然结果一样,但是是在主线程执行的,这里的t.start()是首先创建一个新线程,而后调用我们的run()方法,从而实现多线程编程。

此外,此代码可以使用匿名内部类来进行简化:

java 复制代码
//继承 Thread, 重写 run, 使用匿名内部类
public class Demo3 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                System.out.println("线程运行");
            }
        };
    }
}

1.2 实现Runnable接口

java 复制代码
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("线程运行");
    }
}

可以看到和上面那个实现方法是十分相似的。

但是在main方法里却不能创建Runnable实例然后调用start,在Runnable中没有start方法,所以需要搭配Thread来启动线程。

java 复制代码
public class Demo2 {
    public static void main(String[] args) {
        //创建runnable对象
        Runnable runnable = new MyRunnable();
        //创建线程对象,传入runnable对象
        Thread t = new Thread(runnable);
        t.start();
    }
}

在创建thread对象时,将runnable实例传入。

同样 也可以使用匿名内部类:

java 复制代码
//实现 Runnable, 重写 run, 使用匿名内部类
public class Demo4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("线程运行");
            }
        });
        t.start();
    }
}

1.3 使用lambda表达式

在实际应用中,使用最多的就是这种方法:

java 复制代码
    //使用 lambda 表达式
public class Demo5 {
    public static void main(String[] args) {
        //使用 lambda 表达式创建线程对象
        Thread t = new Thread(() ->{
            System.out.println("线程运行");
        });
        t.start();
    }
}

2.线程中断

线程启动之后,如果遇到一些问题,不想让线程继续运行了,就需要停止线程,也就是线程中断。

2.1 使用变量中断线程

创建一个boolean变量,来控制线程的终止:

java 复制代码
public class Demo9 {
    private static boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (flag) {
                System.out.println("子线程运行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        System.out.println("主线程运行");
        Thread.sleep(2000);
        flag = false;
        System.out.println("让t线程停止");
    }

}

可以观察出,两秒后,flag变为false,子线程终止,同时运行到最后,主线程也终止。

2.2 使用内置的标志位

Thread类中本身就有一个boolean变量,通过调用isInterruptted()方法来判断,如果为false,说明线程并没有中断,如果为Ture,则说明有事件尝试将线程中断。

java 复制代码
public class Demo10 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
           while (!t.isInterrupted()) {
               
           }
        });
    }
}

如果直接这样调用,会出现问题,提示t未初始化,这是因为在lambda表达式中,先执行箭头右边的语句,也就是说在t初始化之前我们就执行了t中的方法,所以需要换一种写法:

java 复制代码
public class Demo10 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
           while (!Thread.currentThread().isInterrupted()) {

           }
        });
    }
}

使用Thread.currentThread()来获取当前线程的引用。

使用interrupt()方法来尝试中断线程

java 复制代码
public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
           while (!Thread.currentThread().isInterrupted()) {
               System.out.println("线程运行中");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        t.start();
        Thread.sleep(2000);
        //尝试中断线程
        t.interrupt();
    }
}

运行后,发现没有成功中断进程

这是由于interrupt()可以唤醒正在"sleep"的线程,报错也显示sleep被打断,打断之后判断中断的标志位改成了false,一直满足while的条件,所以循环不会终止,一直运行。

可以这样解决:

在触发异常后break掉while语句

java 复制代码
public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
           while (!Thread.currentThread().isInterrupted()) {
               System.out.println("线程运行中");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
                   break;
               }
           }
        });
        t.start();
        Thread.sleep(2000);
        //尝试中断线程
        t.interrupt();
    }
}

3.线程等待

由于多线程在运行的时候,是随机进行调度的,有时候一个线程的任务需要等另一个线程结束后,才能进行工作,这个时候就需要进行线程等待了。

线程等待能够将多个线程按照规定的顺序运行。

我们使用join()方法来实现。

java 复制代码
public class Demo11 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            //循环运行五次
            for (int i = 0; i < 5; i++) {
                System.out.println("线程运行中");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                }
            }
        });
        //运行
        t.start();
        System.out.println("主线程运行前");
        //main线程等待t线程结束
        t.join();
        System.out.println("主线程运行后");
    }

}

我们来看看结果:

可以看到,主线程在等待t运行结束,在t线程运行结束后,主线程开始运行。所以t.join()实际上就是t线程被主线程等待。

我们也可以做到t线程等待main线程:

java 复制代码
public class Demo12 {
    public static void main(String[] args) {
        // 获取main引用
        Thread main = Thread.currentThread();
        Thread t = new Thread( () -> {
            try {
                System.out.println("t线程等待中");
                main.join();
                System.out.println("t线程运行中");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程运行中");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

效果:

可以给join()方法添加一个参数:

这里的参数表示等待的最大时间(毫秒),如果时间到了,就不进行等待,直接运行。

还可以增加一个参数,表示纳秒,能够更加精确。

等待时间有一定误差,大概在十毫秒左右

4.线程休眠和获取线程引用

使用sleep()方法实现线程休眠,在上面多次提到,不再赘述

线程引用也在上面体现(Thread.currentThread())。

总结

本篇文章主要介绍了Java中Thread类的一些基本用法和概念,希望能够帮助你们更加理解Java的多线程操作。

相关推荐
Python私教15 分钟前
Java手写链表全攻略:从单链表到双向链表的底层实现艺术
java·python·链表
小麟有点小靈28 分钟前
VSCode写java时常用的快捷键
java·vscode·编辑器
程序猿chen39 分钟前
JVM考古现场(十九):量子封神·用鸿蒙编译器重铸天道法则
java·jvm·git·后端·程序人生·java-ee·restful
&白帝&1 小时前
java HttpServletRequest 和 HttpServletResponse
java·开发语言
阿杆1 小时前
🤯我写了一套无敌的参数校验组件④ | 现已支持 i18n
java·spring
小样vvv1 小时前
【微服务管理】注册中心:分布式系统的基石
java·数据库·微服务
amagi6001 小时前
Java中的正则表达式(Regular Expression)
java
喵手2 小时前
如何快速掌握 Java 反射之获取类的字段?
java·后端·java ee
AronTing2 小时前
06- 服务网格实战:从 Istio 核心原理到微服务治理升级
java·后端·架构
奋进的小暄2 小时前
贪心算法(18)(java)距离相等的条形码
java·开发语言·贪心算法