【JAVAEE】创建线程的方式及线程的常用方法

news2024/11/20 4:51:57

目录

1.创建线程的四种方式

1.1继承Thread类

1.2实现Runnable接口

1.3匿名内部类

1.4lambda表达式

2.多线程的优势-增加运行速度

3.Thread类及常用方法

3.1构造方法

3.2常见属性

演示后台线程

演示线程是否存活

3.3线程中断

3.4线程等待-join()

3.5获取当前线程

3.6启动线程-start()

4.线程状态


1.创建线程的四种方式

Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。这里有四种方式可以实现Java创建线程:

1.继承Thread类并重写run()方法

2.实现Runnable接口并重写run()方法

3.通过匿名内部类的方式创建Thread和实现Runnable

4.通过lambda表达式来实现一个线程

1.1继承Thread类

Thread类是JDK中提供的表示线程的类。

重写run()方法,让线程执行指定的任务。

package lesson01;

public class Demo01_Thread {
    public static void main(String[] args) {
        //创建自己定义的线程对象
        MyThread thread=new MyThread();
        //执行这个线程start方法是启动线程,并通知操作系统加入CPU调度
        thread.start();
    }
}
class MyThread extends Thread{
    //run方法中的代码,就表示线程要执行的任务

    @Override
    public void run() {
        System.out.println("hello thread...");
    }
}

调用start()方法之后,JVM会调用系统API,并在系统中生成一个PCB来执行run()方法中的代码。

1.2实现Runnable接口

使用runnable接口定义任务的好处:

1.解耦(即 把不同的功能都给分开,如果要修改或查找相应的功能的时候可以直接指定的位置找),把定义线程,与定义任务分开。

2.把创建线程,与定义任务分开,以便修改代码时,可以统一修改。

package lesson01;

public class Demo03_Runnable {
    public static void main(String[] args) {
        //创建Runnable对象
        MyRunnable runnable=new MyRunnable();
        //创建线程
        Thread thread=new Thread(runnable);
        //启动线程,参与CPU调度
        thread.start();
    }
}
//实现runnable接口
class MyRunnable implements Runnable{

    @Override
    public void run() {
        while(true){
            System.out.println("生产皮包,金币+1...");
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

1.3匿名内部类

通过匿名内部类的方式创建线程。

package lesson01;
//通过匿名内部类的方式创建线程
public class Demo05_Thread_Anon {
    public static void main(String[] args) {
        Thread thread=new Thread(){
            @Override
            public void run() {
                while(true){
                    System.out.println("hello thread");
                    try{
                        Thread.sleep(1000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        };
        thread.start();
    }
}

通过匿名内部类的方式实现Runnable接口。

package lesson01;

public class Demo06_Runnable_Anon {
    public static void main(String[] args) {
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("hello runnable");
                    try{
                        Thread.sleep(1000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();
    }
}

1.4lambda表达式

要使用lambda表达式实现接口,接口必须是一个函数式接口(即接口定义中,只有一个方法)。

格式:()->{要执行的代码块}

package lesson01;

public class Demo07_Lambda {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            while(true){
                System.out.println("hello lambda...");
                try{
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }
}

2.多线程的优势-增加运行速度

使用多线程编程主要是为了充分利用CPU资源,提升程序效率。

示例:分别对两个变量进行10亿次的自增。

1.串行:单线程

2.并行:多线程

定义次数:

private static long COUNT=10_0000_0000L;

串行操作,单线程:

private static void serial() {
        //记录开始时间
        long begin=System.currentTimeMillis();
        //第一个变量
        long a=0l;
        for(int i=0;i<COUNT;i++){
            a++;
        }
        long b=0l;
        for(int i=0;i<COUNT;i++){
            b++;
        }
        long end=System.currentTimeMillis();
        System.out.println("串行总耗时:"+(end-begin));
    }

并行操作,多线程:

private static void concurrency() throws InterruptedException{
        //记录开始时间
        long begin=System.currentTimeMillis();
        //创建第一个线程
        Thread t1=new Thread(()->{
            long a=0l;
            for(int i=0;i<COUNT;i++){
                a++;
            }
        });
        //第二个线程
        Thread t2=new Thread(()->{
            long b=0l;
            for(int i=0;i<COUNT;i++){
                b++;
            }
        });
        //启动线程
        t1.start();
        t2.start();
        //等待线程执行完成
        t1.join();
        t2.join();

        //结束时间
        long end=System.currentTimeMillis();
        System.out.println("并行总耗时:"+(end-begin));
    }

来看看结果吧:

 并行执行时间确实比串行执行时间短,可却不是串行的1/2,是因为每创建一个线程都是要消耗时间和资源的。

当把计算量改小一点:

private static long COUNT=10_000L;

结果就变成了:

所以并不是所有场景都适合使用多线程,是否使用需根据计算量来确定。

3.Thread类及常用方法

3.1构造方法

方法
说明
Thread()
创建线程对象
Thread(Runnable target)
使用 Runnable 对象创建线程对象
Thread(String name)
创建线程对象,并命名
Thread(Runnable target, String name)
使用 Runnable 对象创建线程对象,并命名
【了解】 Thread(ThreadGroup group,Runnable target)
线程可以被用来分组管理,分好的组即为线程组
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

想要获取当前线程的名字,可以用下面这个方法:

Thread.currentThread().getName()

3.2常见属性

属性
获取方法
ID
getId()
名称
getName()
状态
getState()
优先级
getPriority()
是否后台线程
isDaemon()
是否存活
isAlive()
是否被中断
isInterrupted()
  • ID是线程的唯一标识,不同线程不会重复。
  • 名称是各种调试工具用的到
  • 状态表示线程当前所处的一个情况
  • 优先级高的线程理论上来说更容易被调度
  • 后台线程,JVM会在一个进程的所有非后台线程结束后,才会结束运行
  • 是否存活,简单理解,就是run()方法是否运行结束了

代码示例:创建一个名为”大圣“的线程,查看它的各种属性

package lesson02;

public class Demo10 {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            while(true){
                System.out.println("hello thread...");
                try{
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        },"大圣");
        thread.start();

        //打印thread对象中的属性
        System.out.println("线程ID="+thread.getId());
        System.out.println("线程名="+thread.getName());
        System.out.println("线程状态"+thread.getState());
        System.out.println("线程优先级"+thread.getPriority());
        System.out.println("线程是否后台"+thread.isDaemon());
        System.out.println("线程是否存活"+thread.isAlive());
        System.out.println("线程是否中断"+thread.isInterrupted());
    }
}

演示后台线程

创建线程时,默认是前台线程。

这里在线程启动前传入true设置线程为后台线程。

//在线程启动之前设置线程是否为后台
        thread.setDaemon(true);

代码示例:将线程设置为后台线程,main方法中是一个循环

package lesson02;

public class Demo11_Daemon {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while(true){
                System.out.println("hello thread...");
                //休眠一秒钟
                try{
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        });

        //在线程启动之前设置线程是否为后台
        thread.setDaemon(true);
        System.out.println("是否存活"+thread.isAlive());
        //启动线程
        thread.start();
        //休眠一会儿,确保PCB被创建成功
        Thread.sleep(500);
        System.out.println("是否存活"+thread.isAlive());
        System.out.println("main线程执行完成");
        System.out.println("是否存活"+thread.isAlive());
    }
}

 结果:

 程序退出了,因为main方法运行完了,后台线程影响不了程序是否退出,前台线程可以影响。

演示线程是否存活

演示线程启动前后的状态。

package lesson02;

public class Demo12_Alive {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            int count=0;
            while(true){
                System.out.println("hello thread...");
                try{
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //执行5次后退出线程
                count++;
                if(count==5){
                    break;
                }
            }
        });
        System.out.println("线程启动前的状态:"+thread.isAlive());
        thread.start();
        System.out.println("线程启动后的状态:"+thread.isAlive());
        
        try{
            //等待子线程执行完成
            thread.join();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("线程执行完成的状态:"+thread.isAlive());
        System.out.println("主线程执行完成");
    }
}

看一看结果吧:

thread.isAlive()指的是系统的线程(PCB)是否存活,并不是我们new出来的Thread对象。

3.3线程中断

线程中断指的是停止或中断当前线程的任务

1.通过是否中断的标志位

    //定义一个中断标识
    private static boolean isQuit=false;

子线程处理任务3秒后,将标志位改为true,线程停止。

package lesson02;

public class Demo13_Interrupted01 {
    //定义一个中断标识
    private static boolean isQuit=false;

    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while(!isQuit){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行结束");
        });
        //启动线程
        thread.start();
        //主线程休眠3秒,模拟子线程正在处理任务
        Thread.sleep(3000);
        //设置中断标志位为true
        isQuit=true;
        //让子线程先结束
        Thread.sleep(1000);
        System.out.println("主线程执行结束");
    }
}

2.通过调用Thread类提供的Interrupted方法来中断

System.out.println("是否中断:"+Thread.currentThread().isInterrupted());

默认为false。

示例代码:
 

package lesson02;

public class Demo14_Interrupted02 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //异常处理中断线程
                    //方式一:什么都不做
                    //方式二:处理具体的逻辑
                    //方式三:真正的中断
                    break;
                }
            }
            System.out.println("是否中断:"+Thread.currentThread().isInterrupted());
            System.out.println("线程任务结束");
        });
        //启动线程
        thread.start();
        //主线程休眠3秒,模拟子线程正在处理任务
        Thread.sleep(3000);
        //中断线程,修改Thread中的中断标志
        thread.interrupt();
        //让子线程先结束
        Thread.sleep(1000);
        System.out.println("主线程执行结束");
    }
}

运行结果:

 sleep interrupted报错原因:

当线程在sleep或是阻塞状态的时候,调用interrupted方法,会中断当前的sleep休眠状态,并抛出异常。只有当线程在运行状态时,才会真正结束线程。

3.4线程等待-join()

join方法:明确等待线程的结束

方法
说明
public void join()
等待线程结束
public void join(long millis)
等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)
同理,但可以更高精度

代码示例:

package lesson02;

public class Demo15_Join {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            for(int i=0;i<5;i++){
                System.out.println("hello thread"+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行结束");
        });
        //启动线程
        thread.start();

        System.out.println("join之前,线程状态:"+thread.getState());
        System.out.println("join之前,是否存活:"+thread.isAlive());
        //使用join等待thread线程结束
        try{
            thread.join();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("join之后,线程状态:"+thread.getState());
        System.out.println("join之后,是否存活:"+thread.isAlive());
        System.out.println("主线程执行完成");
    }
}

运行结果:

3.5获取当前线程

方法
说明
public static Thread currentThread();
返回当前线程的引用
public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   }
}

3.6启动线程-start()

run()方法和start()方法的区别:

run()方法:定义线程要执行的任务

start()方法:申请一个真正的系统线程(线程开始运行)

1.直接调用run()方法,并不会取申请一个真正的系统线程(PCB),只是单纯的调用对象的方法。

2.调用start()方法,JVM会调用本地方法取系统中真正的申请一个线程(PCB),并执行run()方法中的逻辑。

示例代码:

start()方法:

package lesson02;

public class Demo16_StartRun {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            while(true){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //启动线程
        thread.start();
        System.out.println("线程状态:"+thread.getState());
        System.out.println("主线程结束");
    }
}

结果:

run()方法:

package lesson02;

public class Demo16_StartRun {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            while(true){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //启动线程
        thread.run();
        System.out.println("线程状态:"+thread.getState());
        System.out.println("主线程结束");
    }
}

结果:

4.线程状态

线程状态指的是Thread对象的状态,不是PCB的状态。(Thread对象有自己的生命周期)

状态解释
NEW创建了一个Java线程,但还未调用start()方法
RUNNABLE运行或在就绪队列中
TERMINATED线程执行完成,PCB在操作系统中已销毁,但是Java对象还在
TIMED_WATING等待一段时间(有时间限制)
WAITING没有时间限制的等待
BLOCK等待锁的时候进入的阻塞状态

示例代码:

package lesson02;

public class Demo17_State {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            //第一种情况
            for(int i=0;i<1000_0000_00;i++){
                //啥也不干,等着让它结束
            }
            System.out.println("线程结束");
        });
        //启动之前打印一下线程状态
        //当前只是New出来了一个Thread类的对象
        System.out.println("启动之前的状态:"+t1.getState());
        //真正启动线程
        t1.start();
        System.out.println("启动之后的状态:"+t1.getState());
        //等待线程结束
        t1.join();
        System.out.println("PCB是否存活:"+t1.isAlive());
        System.out.println("线程执行完成后的状态:"+t1.getState());
    }
}

运行结果:

可以看到线程创建完成,但还没启动时状态为new,启动之后正在运行,状态为runnable,任务执行完成之后,PCB销毁,状态为terminated。

补充:

状态图

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/491933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

23年5月高项备考学习笔记——信息系统管理

管理&#xff1a;监督系统的设计和结构 系统&#xff1a;提供蓝图 系统管理&#xff1a; 规划和组织&#xff1a;业务战略、组织机制、信息系统 业务战略&#xff1a; 总成本领先战略 差异性战略 专注化战略 设计和实施&#xff1a;战略转成需求&#xff0c;便管理&#xff1b…

5月5号软件资讯更新合集.....

Visual Studio Code 1.78 发布 VS Code 1.78 已发布&#xff0c;此版本一些主要亮点包括&#xff1a; 辅助功能改进 - 更好的屏幕阅读器支持、新的音频提示。 新的颜色主题 - “Modern” 浅色和深色主题默认设置。 配置文件模板 - Python、Java、数据科学等的内置模板。 新…

2023年6月DAMA-CDGA/CDGP数据治理认证报名请尽早啦!

6月18日DAMA-CDGA/CDGP数据治理认证考试开放报名中&#xff01; 考试开放地区&#xff1a;北京、上海、广州、深圳、长沙、呼和浩特、杭州、南京、济南、成都、西安。其他地区凑人数中… DAMA-CDGA/CDGP数据治理认证开班时间&#xff1a;5月7日 DAMA认证为数据管理专业人士提供…

线上FullGC问题排查实践——手把手教你排查线上问题 | 京东云技术团队

作者&#xff1a;京东科技 韩国凯 一、问题发现与排查 1.1 找到问题原因 问题起因是我们收到了jdos的容器CPU告警&#xff0c;CPU使用率已经达到104% 观察该机器日志发现&#xff0c;此时有很多线程在执行跑批任务。正常来说&#xff0c;跑批任务是低CPU高内存型&#xff0c…

【马蹄集】第九周作业

第九周作业 目录 MT2125 一样的虫子MT2126 AB数对MT2127 权值计算MT2128 黑客小码哥MT2129 来给单词分类 MT2125 一样的虫子 难度&#xff1a;黄金    时间限制&#xff1a;1秒    占用内存&#xff1a;128M 题目描述 有 N 只虫子&#xff0c;每只虫子有6条腿&#xff0c;每…

通过Python的pytesseract库识别图片中的文字

文章目录 前言一、pytesseract1.pytesseract是什么&#xff1f;2.安装pytesseract3.查看pytesseract版本4.安装PIL5.查看PIL版本 二、Tesseract OCR1.Tesseract OCR是什么&#xff1f;2.安装Tesseract OCR3.安装 Tesseract OCR 语言包 三、使用方法1.引入库2.打开图片文件3.使用…

聊一聊 Valgrind 监视非托管内存泄露和崩溃

一&#xff1a;背景 1. 讲故事 只要是程序总会出现各种莫名其妙的问题&#xff0c;比如&#xff1a;非托管内存泄露&#xff0c;程序崩溃&#xff0c;在 Windows 平台上一般用微软自家的官方工具 App Verifier 就可以洞察&#xff0c;那问题出在 Linux 上怎么办呢&#xff1f…

块状链表实现BigString大字符串操作(golang)

前言 块状链表是介于链表和数组之间的数据结构&#xff0c;能够在 O ( n ) O(\sqrt{n}) O(n ​)时间内完成插入、删除、访问操作。 数据结构如图所示。假设最大容量为 n n n, 则它有一个长度为 s n s\sqrt{n} sn ​的链表。链表中每个结点是一个长度为 2 n 2 \times \sqrt{…

C#_各类应用程序

目录 前言 1. 第一个程序&#xff1a;Hello&#xff0c;World&#xff01; 1.1 Console 1.2 Windows Forms 1.3 WPF&#xff08;Windows Presentation Foundation&#xff09; 1.4 ASP.NET Web Forms 1.5 ASP.NET MVC(Model - View - Controller) 1.6 Windows Store A…

Tuxera NTFS2023Mac专业NTFS驱动软件 解决Mac不能写入移动硬盘U盘问题 管理修复磁盘

Tuxera NTFS2023Mac专业NTFS驱动软件 解决Mac不能写入移动硬盘U盘问题 管理修复磁盘问题! NTFS For Mac2023是一款功能强大的MAC读写软件。NTFS For Mac可以帮助用户对磁盘进行日常管理&#xff0c;如果用户电脑的磁盘有问题&#xff0c;可以使用该软件进行修复&#xff0c;延…

Chatgpt系列(一) 如何使用chatgpt

系列文章目录 第一章: 如何使用chatgpt 第二章: chatgpt的概述 第三章: langchain入门 第四章: index 第五章: prompt 目录 系列文章目录 前言 一、LLM是什么&#xff1f; 二、使用步骤 1.学习地址 2.阅读内容重点的介绍 2.答复 读入数据 总结 系列文章目录前言一…

0基础学Java必备的50个知识点

1、编写&#xff1a;编写的Java代码保存在以“.java”结尾的源文件中。 2、编译&#xff1a;使用javac.exe命令编译java源文件&#xff0c;生成字节码文件。 格式&#xff1a;javac 源文件名.java 3、运行&#xff1a;使用java.exe命令解释运行字节码文件。格式&#xff1a;…

Linux笔记3

目录 一、root用户1.su命令2.sudo命令 二、vi/vim编译器1.三种工作模式2.命令模式3.底线命令模式 三、用户和用户组1.用户组管理2.用户管理3.getent命令 四、权限1.查看权限控制信息2.chmod 命令3.chown 命令 五、常用快捷键1.Ctrlc2.Ctrld3.历史命令4.光标移动快捷键 一、root…

亚马逊云科技和安恒信息,发布云原生SaaS主机安全和云原生堡垒机

4月19日&#xff0c;安恒信息首次举行了以“新见未来 实现梦想”为主题的年度新品发布会。来自产业界、投资界、财经界、媒体界等多方代表共同见证了本次发布会。这也是安恒信息自成立以来&#xff0c;首次大规模、高密度地发布新品。 联合产品发布 云原生SaaS主机安全与云原…

Linux 指令(一)+完整思维导图+实图例子+深入细节+通俗易懂建议收藏

绪论 在上一章&#xff0c;我们已经将Linux环境的安装起来了&#xff0c;从本章开始&#xff0c;我们将正式的进入到Linux的学习&#xff0c;Linux的学习还是比较的枯燥无味的&#xff0c;但我们要吃得苦中苦&#xff0c;让我们一起加油&#xff0c;进大厂拿到心仪的offer&…

黑马程序员-职工管理系统实战-附加源码Git

1、管理系统需求 职工管理系统可以用来管理公司内所有员工的信息 本教程主要利用C来实现一个基于多态的职工管理系统 公司中职工分为三类&#xff1a;普通员工、经理、老板&#xff0c;显示信息时&#xff0c;需要显示职工编号、职工姓名、职工岗位、以及职责 普通员工职责…

Redis在项目实践中的问题解决方案汇总

前言 无论是在开发过程中还是在准备跑路的面试过程中&#xff0c;和Redis相关的话题&#xff0c;难免会涉及到四个特殊场景&#xff1a;缓存穿透、缓存雪崩、缓存击穿以及数据一致性。 虽然在作为服务缓存层的时候Redis确实能极大减少服务端的请求压力&#xff0c;但是如果在…

企业组织管理神器:红海云可视化组织管理功能深度解析

在当前的VUCA时代&#xff0c;企业需要保持敏捷以应对变革和不确定性。组织架构作为承载战略目标的重要工具&#xff0c;如果无法敏捷调整&#xff0c;会直接影响企业战略的成功落地。但组织架构的设计和调整会触及其他业务&#xff0c;包括岗位、编制、人员与汇报关系等信息变…

优先级队列(大根堆与小根堆)

优先级队列&#xff08;大根堆与小根堆&#xff09; 文章目录 优先级队列&#xff08;大根堆与小根堆&#xff09;堆的介绍模拟堆以数组模型为例&#xff0c;创建堆向下调整&#xff08;shiftDown&#xff09;入队&#xff08;push&#xff09;及向上调整&#xff08;shiftUp&a…

java获取文件夹下所有文件名

在进行 Java编程的过程中&#xff0c;我们会经常使用到文件夹下的所有文件名。有时候可能不太熟悉 Java编程的小伙伴们会发现&#xff0c;在代码中没有获取到所有的文件名&#xff0c;那么这个时候我们应该怎么去获取到这些文件呢&#xff1f;在进行 Java编程的过程中&#xff…