问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

Java多线程之线程的创建与Thread类的使用

发布网友 发布时间:2024-09-26 16:51

我来回答

1个回答

热心网友 时间:2024-11-18 00:34

??前面的话??

本篇文章介绍java多线程中Thread类的使用以及有关线程对象创建和常用方法。

?博客主页:未见花闻的博客主页\ ?欢迎关注?点赞?收藏??留言?\ ?本文由未见花闻原创!\ ?掘金首发时间:?2022年5月30日?\ ??坚持和努力一定能换来诗与远方!\ ?参考书籍:?《计算机操作系统》\ ?参考在线编程网站:?牛客网?力扣\ 博主的码云gitee,平常博主写的程序代码都在里面。\ 博主的github,平常博主写的程序代码都在里面。\ ?作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!

?1.线程与Thread类?1.1操作系统中的线程与Java线程??1.1.1线程与Thread类

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库). \ Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装. 也就是说Thread类的一个实例就对应着一个线程。

??1.1.2Thread类的构造方法序号方法名解释1public Thread()无参数构造方法2public Thread(Runnable target)传入实现Runnable接口的对象(任务对象)构造线程3public Thread(Runnable target, String name)根据目标任务并指定线程名创建线程4public Thread(ThreadGroup group, Runnable target)根据线程组和任务创建线程(了解)5public Thread(ThreadGroup group, Runnable target, String name)比构造方法4多一个指定线程名6public Thread(String name)指定线程名创建线程7public Thread(ThreadGroup group, String name)根据线程组并指定线程名创建线程8public Thread(ThreadGroup group, Runnable target, String name,long stackSize)构造函数与构造方法5相同,只是它允许指定线程堆栈大小

? 注:线程可以被用来分组管理,分好的组即为线程组,Runnable类表示任务类,也就是线程需执行的任务。

??1.1.3启用java线程必会的方法

想要使用java线程至少得知道Thread类中这几个方法: |方法名|解释| |---|---| | public void run()|该方法用来封装线程运行时执行的内容| |public synchronized void start()|线程创建并执行run方法| |public static native void sleep(long millis) throws InterruptedException|使线程休眠millis毫秒|

创建Thread对象,必须重写run方法,因为你创建一个线程肯定要用运行一些代码嘛。

?1.2第一个Java多线程程序

首先,我们可以创建一个MyThread类继承Thread类,并重写run方法。

class?MyThread?extends?Thread{????//重写run方法????@Override????public?void?run()?{????????System.out.println("你好!线程!");????}}public?class?TestDemo?{????public?static?void?main(String[]?args)?{????????//创建MyThread线程对象,但是线程没有创建????????Thread?thread?=?new?MyThread();????????//线程创建并运行????????thread.start();????}}

使用new创建线程对象,线程并没有被创建,仅仅只是单纯地创建了一个线程对象,运行start方法时才会创建线程并执行run方法。\ 运行结果:

?1.3使用Runnable对象创建线程

除了使用子类继承Thread类并重写run方法,使用子类实现Runnable接口(该接口中也有一个run方法,表示任务的内容),该对象可以理解为“任务”,也就是说Thread对象可以接受Runnable引用,并执行Runnable引用的run方法。

因为Runable是一个接口,所以需要实现run方法,线程Thread对象创建好后,此时线程并没有创建运行,需要调用start方法来创建启动线程。

class?MyRunnable?implements?Runnable?{????@Override????public?void?run()?{????????System.out.println("使用Runnable描述任务!");????}}public?class?TestDemo3?{????public?static?void?main(String[]?args)?{????????//将Runnable任务传给Thread对象来创建运行线程????????Runnable?runnable?=?new?MyRunnable();????????Thread?thread?=?new?Thread(runnable);????????thread.start();????}}

运行结果:根据“低内聚,高耦合”的编程风格,使用Runnable的方式创建更优。

?1.4使用内部类创建线程

当然也可以使用匿名内部类,来传入匿名对象来重写run方法。

public?class?TestDemo4?{????public?static?void?main(String[]?args)?{????????Thread?thread?=?new?Thread()?{????????????@Override????????????public?void?run()?{????????????????System.out.println("使用匿名内部类创建线程匿名对象");????????????}????????};????????thread.start();????}}

运行结果:

?1.5使用Lambda表达式创建线程

使用Lambda表达式,本质还是使用匿名内部类创建的Thread。

public?class?TestDemo6?{????public?static?void?main(String[]?args)?{????????Thread?thread?=?new?Thread(()?->?System.out.println("使用Lambda表达式表示匿名内部类来创建匿名任务"));????????thread.start();????}}

运行结果:

?1.6多线程并发执行简单演示

在一个进程中至少会有一个线程,如果不使用多线程编程,一个进程中默认会有执行main方法的main线程(该线程是自动创建的),当你创建一个新的线程t,该线程会与main线程并发执行。

public?class?TestDemo7?{????public?static?void?main(String[]?args)?{????????//thread?线程????????Thread?thread?=?new?Thread(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????for?(int?i?=?0;?i?<?10;?i++)?{????????????????????System.out.println("thread线程执行中!");????????????????????//为了使效果更加明显?可以使用sleep方法设定线程睡眠时间????????????????????try?{????????????????????????Thread.sleep(1000);//每执行一次循环就睡眠1秒????????????????????}?catch?(InterruptedException?e)?{????????????????????????e.printStackTrace();????????????????????}????????????????}????????????}????????});????????thread.start();????????//main?线程????????for?(int?i?=?0;?i?<?10;?i++)?{????????????System.out.println("main线程执行中!");????????????//为了使效果更加明显?可以使用sleep方法设定线程睡眠时间????????????try?{????????????????Thread.sleep(1000);//每执行一次循环就睡眠1秒????????????}?catch?(InterruptedException?e)?{????????????????e.printStackTrace();????????????}????????}????}}

运行结果:

从上面的运行结果可以看出一个问题,因为thread线程与main线程都是每打印一句语句线程休眠1秒,两个线程唤醒的先后顺序是随机的,这也是java多线程中的一个“万恶之源”,这个问题给我们带来了很多麻烦,细节等后续的博客细说。

?1.7多线程并发执行的优势

加入我们现在有一个任务,就是分别将a和b两个变量都自增20亿次,我们来看看使用两个线程和单独使用一个线程分别所需的时间是多少。

public?class?Test?{????private?static?final?long?COUNT?=?20_0000_0000L;????//两个线程????public?static?void?many()?throws?InterruptedException?{????????//获取开始执行时间戳????????long?start?=?System.currentTimeMillis();????????Thread?thread1?=?new?Thread(()?->?{????????????long?a?=?0;????????????for?(long?i?=?0;?i?<?COUNT;?i++)?{????????????????a++;????????????}????????});????????thread1.start();????????Thread?thread2?=?new?Thread(()?->?{????????????long?b?=?0;????????????for?(long?i?=?0;?i?<?COUNT;?i++)?{????????????????b++;????????????}????????});????????thread2.start();????????//等待两个线程结束?再获取结束时的时间戳????????thread1.join();????????thread2.join();????????long?end?=?System.currentTimeMillis();????????//执行时间,单位为毫秒????????System.out.println("多线程执行时间:"?+?(end?-?start)?+?"ms");????}????//单线程????public?static?void?single()?{????????//记录开始执行的时间戳????????long?start?=?System.currentTimeMillis();????????long?a?=?0;????????for?(long?i?=?0;?i?<?COUNT;?i++)?{????????????a++;????????}????????long?b?=?0;????????for?(long?i?=?0;?i?<?COUNT;?i++)?{????????????b++;????????}????????//获取执行结束时的时间戳????????long?end?=?System.currentTimeMillis();????????System.out.println("单线程执行时间:"?+?(end?-?start)?+?"ms");????}????public?static?void?main(String[]?args)?throws?InterruptedException?{????????//多线程????????many();????????//单线程????????single();????}}

我们来看看完成这个任务所需的时间:根据结果我们发现两个线程并发执行的时间大约是500ms左右,单线程执行的时间大约是1000ms左右,当然如果任务量不够大,可能多线程相比于单线程并不会有优势,毕竟创建线程本身还是有开销的。

?2.Thread类的常用属性与方法?2.1Thread类中的重要属性属性获取该属性的方法线程的唯一标识IDpublic long getId()线程的名称namepublic final String getName()线程的状态statepublic State getState()线程的优先级prioritypublic final int getPriority()线程是否后台线程public final boolean isDaemon()线程是否存活public final native boolean isAlive()线程是否中断public boolean isInterrupted()

每一个线程都拥有一个id作为标识,其中处于同一进程的所有线程id相同,每个进程间都有唯一的id标识。\ 线程也是拥有名字的,如果我们创建Thread对象时,没有指定线程对象的名称,则会默认命名为Thread-i,其中i为整数。

通过了解进程,我们知道进程拥有3种状态,分别为阻塞,执行和就绪。而java中的线程也有类似与这种状态的定义,后面我们细说,优先级也一样就不用多说了。

java线程分为后台线程与前台线程,其中后台线程不会影响到进程的退出,而前台线程会影响进程的退出,比如有线程t1与线程t2,当这两个线程为前台线程时,main方法执行完毕时,t1与t2不会立即退出,要等到线程执行完毕,整个进程才会退出,反之,当这两个线程为后台线程时,main方法执行完毕时,t1与t2线程被强制结束,整个进程也就结束了。

关于java线程的属性,我们可以通过java官方的jconsole调试工具查看java线程的一些属性。 这个工具一般在jdk的bin目录,双击打开有如下界面:选择需要查看的线程并查看:

选择你需要查看的进程属性:

?2.2Thread类中常用方法总结??2.2.1常用方法方法名解释public void run()该方法用来封装线程运行时执行的内容public synchronized void start()线程创建并执行run方法public static native void sleep(long millis) throws InterruptedException使线程休眠millis毫秒public final void join() throws InterruptedException等待线程结束(在哪个线程中调用哪个对象的join方法,哪个线程就等待哪个对象)public final synchronized void join(long millis) throws InterruptedException等待线程结束,最多等待millis毫秒public final synchronized void join(long millis, int nanos) throws InterruptedException指定最多等待时间等待线程,精确到纳秒public void interrupt()中断线程对象所关联的对象,如果线程在休眠(阻塞状态)会抛出异常通知,否则设置中断标志位public static boolean interrupted()判断当前线程的中断标志位是否设置,调用后会清除线程的中断标志位public boolean isInterrupted()判断当前线程的中断标志位是否设置,调用后不会影响线程的标志位public final synchronized void setName(String name)修改线程对象名称public static native Thread currentThread()获取当前线程对象??2.2.2中断线程

如果我们想中断一个正在执行的线程,该如何做呢?最简单但不严谨的方法就是我们在run方法中定义一个中断标志位(需要中断时标志位为true,默认情况为false),每次执行具体任务时需要先判断中断标志位是否为true,如果是就结束线程,否则继续执行。

public?class?TestDemo8?{????private?static?boolean?isQuit?=?false;????public?static?void?main(String[]?args)?throws?InterruptedException?{????????Thread?thread?=?new?Thread(()?->?{????????????while(!isQuit)?{????????????????//每隔1秒打印一句????????????????System.out.println("一个不起眼的线程!");????????????????try?{????????????????????Thread.sleep(1000);????????????????}?catch?(InterruptedException?e)?{????????????????????e.printStackTrace();????????????????}????????????}????????});????????thread.start();????????//main线程阻塞5秒?按理会打印5句话????????Thread.sleep(5000);????????isQuit?=?true;????}}

运行结果:

但是该方法是不够严谨的,有些场景可能达不到预期的效果,最优的做法就是调整线程对象或者线程类中的自带标志位。

? 方式1:使用Thread对象中的标志位\ 首先使用currentThread方法获取线程对象,然后再调用该对象中的isterrupted方法获取该对象的中断标志位代替我们自己所写的isQuit标志位,然后等该线程运行一段时间后使用interrupt方法改变标志位,中断线程,写出如下代码,看看能不能达到预期效果:

public?class?TestDemo9?{????public?static?void?main(String[]?args)?throws?InterruptedException?{????????Thread?thread?=?new?Thread(()?->{????????????while?(!Thread.currentThread().isInterrupted())?{????????????????System.out.println("又是一个不起眼的线程!");????????????????try?{????????????????????Thread.sleep(1000);????????????????}?catch?(InterruptedException?e)?{????????????????????e.printStackTrace();????????????????}????????????}????????});????????thread.start();????????//main休眠5秒????????Thread.sleep(5000);????????//使用interrupt方法修改线程标志位,使其中断????????thread.interrupt();????}}

我们来看一看:失败了,抛出一个InterruptedException异常后,线程没有中断,而是继续运行,原因是interrupt方法遇到因为调用 wait/join/sleep 等方法而阻塞的线程时会使sleep等方法抛出异常,并且中断标志位不会修改为true,这时我们的catch语句里面值输出了异常信息并没有去中断异常,所以我们需要在catch语句中加上线程结束的收尾工作代码和退出任务循环的break语句就可以了。

public?class?TestDemo9?{????public?static?void?main(String[]?args)?throws?InterruptedException?{????????Thread?thread?=?new?Thread(()?->{????????????while?(!Thread.currentThread().isInterrupted())?{????????????????System.out.println("又是一个不起眼的线程!");????????????????try?{????????????????????Thread.sleep(1000);????????????????}?catch?(InterruptedException?e)?{????????????????????e.printStackTrace();????????????????????//收尾工作????????????????????System.out.println("收尾工作!");????????????????????break;????????????????}????????????}????????});????????thread.start();????????//main休眠5秒????????Thread.sleep(5000);????????//使用interrupt方法修改线程标志位,使其中断????????thread.interrupt();????}}

运行结果:

? 方式2:使用Thread类中的标志位\ 除了isInterrupted,还有一个静态方法interrupted能够访问类中的标志位,一般一个程序中只有一个,我们也可以使用该静态方法来作为中断标志位,然后到时机后使用interrupt方法来中断线程执行。

pub
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
赵玉平名家论坛 赵玉平人物经历 杭州市汽车西站 现在能提前几天售票啊? 请问杭州西站汽车票可以提前几天买? 杭州汽车西站在节假日的时候需要提前买票的吗 散步有助于减肥吗 魑瑟忝箜莫尘读什么 端午送什么 端午节家里要摆放什么东西 龙舟是如何制作的? 正确踩油门的方法介绍 中老年人多的市场门口开个3元店如何 刑事诉讼法第六十条规定什么是取保候审 新刑诉法对取保候审的规定有哪些 新刑诉法取保候审的条件都有什么 苹果内存怎么清理(苹果如何不删除app清理内存) 空调的排风口是多少度? 移动空调哪个出风口排水? 后排空调出风口是什么意思 开空调时前排空调可以,后排出风口没反应,这是什么情况呢? 媒体管家的媒体资源都有哪些? 小米手机横着拍照显示竖屏怎么调整 小米手机横屏拍照如何设置满屏 当代中国大师虽寥寥,然小家众多,试问其中最有大家之范者几何? 世界征服者四演习入侵冰泉打完了该怎么办 世界征服者3营救友军4打法介绍_世界征服者3营救友军4打法是什么 苏东坡传2010版 ...的英文原版小说~~~像《简爱》 《傲慢与偏见》之类的~~~多谢哈... 我不知道将去何方,但我已在路上 求林语堂先生的京华烟云英文原版小说Moment in Peking 无良买家,收到货后一天一借口不确认,第七天还按退款。我出售的是个二 ... 淘宝上系统自动确认收货了,但今天还在七天无理由退款时间内 紫金山在南京哪个区?怎么去? 不用电脑怎么把手机自带的相机拍的视频传快手里 冬天去青海旅游,有什么好的建议与攻略? 问道超级粉水晶在哪里买呢? 缺土缺木的女孩取什么名字好 又缺土又缺木怎么取名 易于记忆和发音的名字 word中怎么设置相同行间距不同? 无线网络连接显示受限制怎么回事? word如何将输入的小写金额快速转换为大写金额word文档如何把小写... 2024天津传媒学院艺术类专业有哪些? 天津师范大学编导专业 天津传媒学院有哪些好的专业? 细石混凝土与混凝土有什么不一样? 日记《关心环卫工人》观后感250字。 哪吒_观后感250字 为什么日本人都喜欢买国产商品,而中国人却更喜欢买进口商品? 中国人买进口商品,是中国挣钱得多还是外国挣得多? 过年期间,六安有哪些适合带孩子去游玩的好去处