java如何创建线程

java如何创建线程

  • 1. java如何创建线程
    • 1.1 通过继承Thread类来创建线程
    • 1.2 通过实现Runnable接口来创建线程
    • 1.3 通过匿名内部类来创建线程
    • 1.4 lambda表达式
    • 1.5 通过实现Runnable接口的方式创建线程目标类的优缺点

1. java如何创建线程

一个线程在Java中使用一个Thread实例来描述。Thread类是Java语言的一个重要的基础类,位于java.lang包中。Thread类有不少非常重要的属性方法用于存储和操作线程的描述信息.

Thread类的构造方法:

在这里插入图片描述

1.1 通过继承Thread类来创建线程

666
666
666
666
(1) 继承Thread类,创建一个新的线程类。

(2) 同时重写run()方法,将需要并发执行的业务代码编写在run()方法中。

代码写法如下:

class MyThread extends Thread {
//注释这个方法重写了父类方法
   @Override
   public void run() {
   //需要并发执行的代码
	  System.out.println("hello t");
	}
}
public class ThreadDemo1 {
    //第一种写法是使用Thread的run描述线程入口
    public static void main(String[] args) {
        Thread t = new MyThread();//向上转型
        t.start();//会创建新的线程
        System.out.println("hello main");
    }
}

此时我们可以运行一下:

在这里插入图片描述
此时呢~
我们就通过start()创建了一个新的线程,并且由于我们调用了start(),所以系统自动帮我们调用了run(),此时的run()是执行在 t 线程里面的.

我们可以通过打开jconsole.exe 这个文件来查看我们java在执行期间运行的线程.

由于线程执行完就会结束,为了方便查看,我们写一个死循环来方便我们观看.

在这里插入图片描述

在这里插入图片描述
此时,红色框框里面的main线程就是我们的主线程,另一个Thread-0就是我们刚才创建的一个 t 线程,由于我们没有指定名字,所以这个线程名字默认从0往后递增~

在这里插入图片描述
红色框框里面的两个构造方法是可以修改这个线程的名字的,先讲第一个红色框框里面的.

我们先在MyThread这个类中,通过快速创建构造方法,将可以传入名字参数的构造方法写出来.
在这里插入图片描述

class MyThread extends Thread {
	//调用父类的构造方法
    public MyThread(String name) {
        super(name);
    }

    @Override
   public void run() {
       while (true){
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("hello t");
       }
    }
}

此时呢?我们就可以对这个线程起名字了.

在这里插入图片描述

1.2 通过实现Runnable接口来创建线程

写一个类MyRunnable实现Runnable接口,覆写run()方法,创建MyRunnable对象runnable,将runnable作为参数调用Thread有参构造,创建线程thread,调用start()启动线程。

我们先查看一下Runnable这个接口.


Runnable有且仅有一个抽象方法 —— void run(),代表被执行的用户业务逻辑的抽象,在使用的时候,将用户业务逻辑编写在Runnable实现类的run()方法中。当Runnable实例传入Thread实例的target属性后,Runnable接口的run()的方法将被异步调用。

如何理解上面这句话呢?

我们先理解第一句话: 将用户业务逻辑编写在Runnable实现类的run()方法中.

在这里插入图片描述

由于我们的MyThread这个类实现了Runnable这个接口,所以我们需要重写这个接口里面的run()方法.如上图.

我们再来理解第二句话: 当Runnable实例传入Thread实例的target属性后,Runnable接口的run()的方法将被异步调用。

首先呢?我们需要new一个Runnable的实例.

 MyRunnable myRunnable = new MyRunnable();

接着又讲到将Runnable的实例传入到Thread实例的target属性.

target是什么?

在Thread类的run()方法中,如果target(执行目标)不为空,就执行target属性的run()方法。而target属性是Thread类的一个实例属性,并且target属性的类型为Runnable

在这里插入图片描述

此时我们的MyRunable的类型就是target的类型(Runnable),所以此时我们实例的 myrunable 就可以作为参数传入到Thread的构造方法中.


class MyRunnable implements Runnable {
    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello t");
        }
    }
}
public class ThreadDemo2 {
    //第二种方法是使用Runnable interface(接口) 来描述线程入口
    public static void main(String[] args) {
    //先实例化一个实现Runnable接口的类
        MyRunnable myRunnable = new MyRunnable();
        //将这个类的引用作为参数传入Thread的构造方法中
        Thread t = new Thread(myRunnable);
        //此时可以通过start()创建一个线程,在这个线程中调用这个MyRunnable这个类中的run()方法.
        t.start();
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello main");
        }
    }
}

当我们运行时:

在这里插入图片描述

当然,我们也可以自己指定线程名字,这里就不进行演示了.

1.3 通过匿名内部类来创建线程

因为有些线程是只需要调用一次的,所以我们可以通过匿名内部类这样的方式来进行run()的重写.

代码如下:

在这里插入图片描述

在实例化这个对象时,在其后面写入一个{},此时就可以在这个{}中写入自己需要的方法.

注意:此时这个类是一次性的类.

上述代码的运行结果是:

在这里插入图片描述
可能有人就会问了,你这次怎么没有main线程啊,其实是因为main线程在执行完t.start()之后后续就没有代码需要执行了,所以自然这个线程就结束了,于是就是我们上述看到的,只有 t 线程.

在实现Runnable编写target执行目标类时,如果target实现类是一次性类,可以使用匿名实例的形式。

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

1.4 lambda表达式

使用Lambda表达式优雅地创建Runnable线程目标类
在这里插入图片描述
我们通过观察这个Runnable接口可以发现,上面有一串注释,@FunctionalInterface 这个注释的意思就是标记这个接口为函数式接口,在Java中,“函数式接口”是有且仅有一个抽象方法的接口。反过来说,如果一个接口中包含两个或两个以上的抽象方法,就不能使用@FunctionalInterface注解,否则编译会报错。

Runnable接口是一个函数式接口,在接口实现时可以使用Lambda表达式提供匿名实现,编写出比较优雅的代码。 如果一个接口中有多个抽象方法,那样没有办法使用Lambda表达式简化。

  public static void main(String[] args) {
        //()里面放参数,{}里面放函数体
        Thread t = new Thread(()->{
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello t");
        }
        },"猪猪侠");
        t.start();
        
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello main");
        }
    }

但是呢? 此时小鱼有一个问题问大家,大家对比五段代码,找到哪几个有错误并且说明错误原因.


public class ThreadDemo5 {
//例一
    static int count = 0;
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while (true){
                System.out.println(count);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}
public class ThreadDemo5 {
    //例二
    public static void main(String[] args) {
        int count = 0 ;
        Thread t = new Thread(()->{
            while (true){
                System.out.println(count);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        count = 5;
        t.start();
        
    }
}
public class ThreadDemo5 {
    //例三
    public static void main(String[] args) {
        int count = 0 ;
        Thread t = new Thread(()->{
            while (true){
                System.out.println(count);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}
public class ThreadDemo5 {
    //例四
    public static void main(String[] args) {
        final int count = 0 ;
        Thread t = new Thread(()->{
            while (true){
                System.out.println(count);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        
    }
}
public class ThreadDemo5 {
    //例五
    public static void main(String[] args) {
        int count = 0 ;
        Thread t = new Thread(()->{
            while (true){
                System.out.println(count++);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}

(1) 第一个没有错误,由于count是成员变量,在线程中是共享资源,所以lambda表达式中是可以访问这个变量并且对其进行修改的.
(2) 第二个有错误,Lambda 表达式(匿名类) 不能访问非 final 的局部变量 .

因为成员变量存在堆中,而局部变量是在栈上分配,存在于虚拟机栈的局部变量表中,Lambda 表达(匿名类) 有可能会在另一个线程中执行。如果在线程中要直接访问一个局部变量,可能线程执行时该局部变量已经被销毁了,而 final 类型的局部变量在 Lambda 表达式(匿名类) 中其实是局部变量的一个拷贝

(3) 第三个没有错误,虽然我们的count是局部变量,但是由于我们的代码并未有对该变量进行后续的修改,我们可以称这个count变量为实际final,意思就是虽然没有被final修饰,但是由于并没有修改这个变量的值,所以在lambda表达式中可以使用.
(4) 没有问题

(5) 错误,lambda表达式不能修改局部变量.原因如下:

lambda表达式不能修改局部变量

1.5 通过实现Runnable接口的方式创建线程目标类的优缺点

通过实现Runnable接口的方式创建线程目标类有以下缺点:

  • 所创建的类并不是线程类,而是线程的target执行目标类,需要将其实例作为参数传入线程类的构造器,才能创建真正的线程。
  • 如果访问当前线程的属性,不能直接访问Thread的实例方法,必须通过Thread.currentThread()获取当前线程实例,才能访问和控制当前线程。

通过实现Runnable接口的方式创建线程目标类有以下优点:

  • 可以避免由于Java单继承带来的局限性。如果异步逻辑所在类已经继承了一个基类,就没有办法再继承Thread类。比如,当一个Cat类继承了Animal类,再要继承Thread类就不行了。所以在已经存在继承关系的情况下,只能使用实现Runnable接口的方式。
  • 逻辑和数据更好分离。通过实现Runnable接口的方法创建多线程更加适合同一个资源被多段业务逻辑并行处理的场景。在同一个资源被多个线程逻辑异步、并行处理的场景中,通过实现Runnable接口的方式设计多个target执行目标类可以更加方便、清晰地将执行逻辑和数据存储分离,更好地体现了面向对象的设计思想。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

android8 rk3399 同时支持多个USB摄像头

文章目录一、前文二、CameraHal_Module.h三、CameraHal_Module.cpp四、编译&烧录Image五、App验证一、前文 Android系统默认支持2个摄像头,一个前置摄像头,一个后置摄像头需要支持数量更多的摄像头,得修改Android Hal层的代码 二、Camer…

VueX快速入门(适合后端,无脑入门!!!)

文章目录前言State和Mutations基础简化gettersMutationsActions(异步)Module总结前言 作为一个没啥前端基础(就是那种跳过js直接学vue的那种。。。)的后端选手。按照自己的思路总结了一下对VueX的理解。大佬勿喷qAq。 首先我们需要…

我的 System Verilog 学习记录(11)

引言 本文简单介绍 SystemVerilog 的其他程序结构。 前文链接: 我的 System Verilog 学习记录(1) 我的 System Verilog 学习记录(2) 我的 System Verilog 学习记录(3) 我的 System Verilo…

Linux lvm管理讲解及命令

♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放&#xff0…

软件行业的最后十年【ChatGPT】

在这篇文章中,我将说明像 ChatGPT 这样的生成式人工智能 (GAI) 将如何在十年内取代软件工程师。 预测被离散化为 5 个阶段,总体轨迹趋向于完全接管。 但首先,一个简短的前言。 推荐:用 NSDT场景设计器 快速搭建3D场景。 1、关于AI…

二叉搜索树:AVL平衡

文章目录一、 二叉搜索树1.1 概念1.2 操作1.3 代码实现二、二叉搜索树的应用K模型和KV模型三、二叉搜索树的性能分析四、AVL树4.1 AVL树的概念4.2 AVL树的实现原理4.3 旋转4.4 AVL树最终代码一、 二叉搜索树 1.1 概念 二叉搜索树( Binary Search Tree,…

LeetCode刷题记录---数位DP算法

😄 学会数位dp算法,可以连杀好几道力扣困难题,加油~ 🚀题目: 难度题目困难2376. 统计特殊整数困难1012. 至少有 1 位重复的数字困难233. 数字 1 的个数困难面试题 17.06. 2出现的次数🚀学习资料: 数位dp算法,我是跟着灵神学的,感谢灵神!数位 dp 通用模板参考灵神…

Python数据分析案例24——基于深度学习的锂电池寿命预测

本期开始案例较为硬核起来了,适合理工科的硕士,人文社科的同学可以看前面的案例。 案例背景 这篇文章是去年就发了,刊物也印刷了,现在分享一部分代码作为案例给需要的同学。 原文链接(知网文章 C核)&…

python如何快速采集美~女视频?无反爬

人生苦短 我用python~ 这次康康能给大家整点好看的不~ 环境使用: Python 3.8 Pycharm mou歌浏览器 mou歌驱动 —> 驱动版本要和浏览器版本最相近 <大版本一样, 小版本最相近> 模块使用: requests >>> pip install requests selenium >>> pip …

不是,到底有多少种图片懒加载方式?

一、也是我最开始了解到的 js方法&#xff0c;利用滚动事件&#xff0c;判断当时的图片位置是否在可视框内&#xff0c;然后进行渲染。 弊端&#xff1a;代码冗杂&#xff0c;你还要去监听页面的滚动事件&#xff0c;这本身就是一个不建议监听的事件&#xff0c;即便是我们做了…

【selenium学习】数据驱动测试

数据驱动在 unittest 中&#xff0c;使用读取数据文件来实现参数化可以吗&#xff1f;当然可以。这里以读取 CSV文件为例。创建一个 baidu_data.csv 文件&#xff0c;如图所示&#xff1a;文件第一列为测试用例名称&#xff0c;第二例为搜索的关键字。接下来创建 test_baidu_da…

百度生成式AI产品文心一言邀你体验AI创作新奇迹:百度CEO李彦宏详细透露三大产业将会带来机遇(文末附文心一言个人用户体验测试邀请码获取方法,亲测有效)

百度生成式AI产品文心一言邀你体验AI创作新奇迹中国版ChatGPT上线发布强大中文理解能力超强的数理推算能力智能文学创作、商业文案创作图片、视频智能生成中国生成式AI三大产业机会新型云计算公司行业模型精调公司应用服务提供商总结获取文心一言邀请码方法中国版ChatGPT上线发…

贪心算法的原理以及应用

文章目录0、概念0.1.定义0.2.特征0.3.步骤0.4.适用1、与动态规划的联系1.1.区别1.2.联系2、例子3、总结4、引用0、概念 0.1.定义 贪心算法&#xff08;greedy algorithm &#xff0c;又称贪婪算法&#xff09;是指&#xff0c;在对问题求解时&#xff0c;总是做出在当前看来是…

Java怎么实现几十万条数据插入(30万条数据插入MySQL仅需13秒)

本文主要讲述通过MyBatis、JDBC等做大数据量数据插入的案例和结果。 30万条数据插入插入数据库验证实体类、mapper和配置文件定义User实体mapper接口mapper.xml文件jdbc.propertiessqlMapConfig.xml不分批次直接梭哈循环逐条插入MyBatis实现插入30万条数据JDBC实现插入30万条数…

第十九天 Maven总结

目录 Maven 1. 前言 2. 概述 2.1 介绍 2.2 安装 3. IDEA集成Maven 3.1 集成Maven环境 3.2 创建Maven项目 3.3 Maven坐标详解 3.4 导入maven项目 4. 依赖管理 4.1 依赖配置 4.2 依赖传递 4.3 依赖范围 4.4 生命周期 4.5 插件 Maven 1. 前言 1). 什么是Maven? …

Linux实操之服务管理

文章目录一、服务(service)管理介绍:service管理指令查看服务名服务的运行级别(runlevel):CentOS7后运行级别说明chkconfig指令介绍一、服务(service)管理介绍: 服务(service)本质就是进程&#xff0c;但是是运行在后台的&#xff0c;通常都会监听某个端口&#xff0c;等待其它…

原力计划来了【协作共赢 成就未来】

catalogue&#x1f31f; 写在前面&#x1f31f; 新星计划持续上新&#x1f31f; 原力计划方向&#x1f31f; 原力计划拥抱优质&#x1f31f; AIGC&#x1f31f; 参加新星计划还是原力计划&#x1f31f; 创作成就未来&#x1f31f; 写在最后&#x1f31f; 写在前面 哈喽&#x…

依赖注入~

依赖注入之setter注入&#xff1a; 依赖注入是IOC具体的一种实现方式&#xff0c; 这是针对资源获取的方式角度来说的&#xff0c;之前我们是被动接受&#xff0c;现在IOC具体的实现叫做依赖注入&#xff0c;从代码的角度来说&#xff0c;原来创建对象的时候需要new&#xff0…

Phoenix基础命令_视图映射和表映射_数字存储问题---大数据之Hbase工作笔记0036

然后我们再来看看,用Phoenix来操作hbase,的基本用法 具体的其他的命令在官网都能找到,这里就说几个 https://phoenix.apache.org/language/index.html 首先是创建表,这里注意,默认表名给弄成大写的 这里的varchar对应的其实就是hbase中的string 然后这里的id表示行的rowkey 可…

chatgpt3.5和chatgpt4的区别

ChatGPT4是基于GPT-3模型的一个实例&#xff0c;但ChatGPT4已经进行了进一步的改进和优化。GPT-3&#xff08;第三代生成式预训练模型&#xff09;是OpenAl开发的一个大型语言模型&#xff0c;它在很多自然语言处理任务中表现出色。ChatGPT4继承了GPT-3的基本架构和能力&#x…
最新文章