Java线程中的常见方法(start方法和run方法)
作者:0x3f3f3f3f 发布时间:2023-11-16 17:41:32
start方法和run方法
$start()$方法用来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到$cpu$时间片,就开始执行$run()$方法。而直接调用$run()$方法,仅仅只是调用了一个类里的方法,其本质上还是在当前线程中执行的,因此只有使用$start()$方法来调用$run()$方法才能实现真正的多线程。
示例代码
@Slf4j(topic = "c.Test4")
public class Test4 {
public static void main(String[] args) {
Thread t1 = new Thread("t1"){
@Override
public void run() {
log.debug("running");
}
};
t1.run();
}
}
上述代码是直接调用的$run()$方法。可以看到打印信息里,是$main$线程执行了这个方法。
@Slf4j(topic = "c.Test4")
public class Test4 {
public static void main(String[] args) {
Thread t1 = new Thread("t1"){
@Override
public void run() {
log.debug("running");
}
};
t1.start();
}
}
而如果使用$start()$方法启动,才是真正的由$t1$线程执行的$run$方法。
注意
需要注意的是,当$Thread$对象调用了$start()$方法后,就会进入就绪状态,处于就绪状态时无法再调用$start()$方法,否则就会抛出$IllegalThreadStateException$异常,如下代码所示
@Slf4j(topic = "c.Test4")
public class Test4 {
public static void main(String[] args) {
Thread t1 = new Thread("t1"){
@Override
public void run() {
log.debug("running");
}
};
t1.start();
t1.start();
}
}
异常信息:
sleep方法与yield方法
sleep
调用$sleep()$方法会让当前线程从$Running$状态变成$Time Waiting$状态(阻塞)
其它线程可以使用$interrupt$方法打断正在睡眠的线程,此时$sleep$方法会抛出InterruptedException
睡眠结束后的线程未必会立刻得到执行
建议用$TimeUnit$的$sleep$代替$Thread$的$sleep$来获得更好的可读性示例代码
@Slf4j(topic = "c.Test5")
public class Test5 {
public static void main(String[] args) {
Thread t1 = new Thread("t1"){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
log.debug("t1 state {}", t1.getState());
//让主线程休眠500ms
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t1 state {}", t1.getState());
}
}
//17:13:21.729 [main] DEBUG c.Test5 - t1 state RUNNABLE
//17:13:22.245 [main] DEBUG c.Test5 - t1 state TIMED_WAITING
上述代码中,首先启动$t1$线程,此时打印线程的状态应该是处于$RUNNABLE$状态,而让主线程休眠是防止主线程先执行打印,但是还未进入到$sleep()$状态。当执行到$run()$里边的$sleep$方法时,线程进入$TIMED WAITING$状态
@Slf4j(topic = "c.Test6")
public class Thread6 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1") {
@Override
public void run() {
try {
log.debug("enter sleep");
Thread.sleep(2000);
} catch (InterruptedException e) {
log.debug("wake up");
e.printStackTrace();
}
}
};
t1.start();
Thread.sleep(1000);
log.debug("interrupt t1");
//被唤醒
t1.interrupt();
}
}
执行结果
上述代码中,当$start$方法启动后,$t1$线程进入睡眠状态,打印提示信息,睡眠时间为$2s$,在$main$线程中睡眠$1s$后打断$t1$线程的睡眠,提示打断信息,并且调用$interrupt()$方法,此时线程被打断,抛出异常。
$TimeUnit$类中新增了以什么单位去睡眠,可读性更好,但是本质上没区别,只是进行了单位换算
TimeUnit.SECONDS.sleep(1);//该语句作用是睡眠一秒
yield
调用$yield$会让当前进程从$Running$进入到$Runnable$就绪状态,然后调度执行其他线程具体的实现依赖于操作系统的任务调度器,(即当任务调度器中没有其他任务时,即使让出$cpu$,也会继续执行该线程)$sleep$执行后是进入阻塞状态,此时睡眠时间不结束,就不会分配$cpu$给该线程,但是$yield$是进入就绪状态,即如果没有其他线程需要执行,那么还会给该线程分配时间片,这是$sleep$和$yield$的最大区别线程优先级
线程优先级
会提示调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略他
如果$cpu$比较忙,那么优先级高的会获得更多的时间片,可$cpu$空闲时,优先级几乎没有
sleep的应用-防止cpu占用100%
在没有利用$cpu$来计算时,不要让$while(true)$空转浪费$cpu$,这时可以可以使用$yield$或者$sleep$来让$cpu$的使用权交给其他程序
while (true) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
可以使用$wait$或者条件变量达到类似的效果
不同的是后两者都需要加锁,并且需要相应的唤醒操作,一般适用于要进行同步的场景
$sleep$适用于无需锁同步的场景
join方法
以下程序的打印结果:
@Slf4j(topic = "c.Test6")
public class Test6 {
static int r = 0;
public static void main(String[] args) {
test();
}
private static void test() {
log.debug("开始");
Thread t = new Thread("t1") {
@Override
public void run() {
log.debug("开始");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("结束");
r = 10;
}
};
t.start();
log.debug("r的值是{}", r);
log.debug("结束");
}
}
因为主线程和$t1$线程是并行的,$t1$线程需要$1s$后才能计算出$r$的值,而主线程一开始就要打印出$r$的值,因此打印的值为0
解决方法:
在$t.start();$后边加上$t.join();$即可。$join$的作用是等待某线程运行结束。
以调用方的角度来说,需要等待结果返回才能继续执行就是同步,不需要等待返回结果就能继续执行的就是异步。
因此$join$方法实际上是让其同步执行
有实效的等待
$join(毫秒)$方法里可以有一个参数是传入等待的时间,如果线程执行时间大于等待时间,则等待时间到了之后,就会停止等待。如果线程执行时间小于等待时间,则线程执行完毕之后,等待也会跟着结束。不会把设置的等待时间过完。
interrupt方法
打断$sleep, wait, join$的线程,即打断阻塞状态的线程
打断$sleep$的线程,会清空打断状态
@Slf4j(topic = "c.Test7")
public class Test7 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread("t1"){
@Override
public void run() {
log.debug("sleep...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.start();
Thread.sleep(1000);
log.debug("interrupt");
t.interrupt();
log.debug("打断标记: {}", t.isInterrupted());
}
}
打断正常运行的线程,不会清空打断状态
因此我们可以在线程中判断打断标记,来决定是否被打断,以及执行被打断之前的收尾工作。
@Slf4j(topic = "c.Test8")
public class Test8 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread("t1"){
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
log.debug("线程被打断了");
break;
}
}
}
};
t.start();
Thread.sleep(1000);
log.debug("interrupt");
t.interrupt();
}
}
守护线程
默认情况下,$java$需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其他非守护线程运行结束了,即使守护线程的代码没有执行完毕,也会强制结束。
@Slf4j(topic = "c.Test10")
public class Test10 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread("t1") {
@Override
public void run() {
while (true) {
}
}
};
//设置线程为守护线程
t.setDaemon(true);
t.start();
Thread.sleep(1000);
log.debug("主线程结束");
}
}
如果不把$t$设置为守护线程,则因为线程内部的死循环,导致程序不会结束运行。
来源:https://blog.51cto.com/u_15487307/5526387


猜你喜欢
- 汉诺(Hanoi)塔问题:古代有一个梵塔,塔内有三个座A、B、C,A座上有n个盘子,盘子大小不等,大的在下,小的在上(如图)。有一个和尚想把
- activity_list.xml文件代码如下:<?xml version="1.0" encoding=&quo
- 这几天做项目,有些地方的图片需要用到圆形图片,所以百度了一下,在github上找到一个开源项目,处理很简单,效果如下:使用起来特别简单,一共
- 本文实例讲述了java实现求两个字符串最长公共子串的方法。分享给大家供大家参考,具体如下:这个是华为OJ上的一道题目。首先,如果我们用jav
- C# 利用代理爬虫网页实现代码:// yanggang@mimvp.com// http://proxy.mimvp.com// 2015-
- 概述事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。Spring Framework对事务管理提供了一致的
- 本文实例为大家分享了Android自定义textview实现跑马灯效果的具体代码,供大家参考,具体内容如下xml布局<?xml ver
- 做了一个项目,首页是使用ResideMenu实现,通过菜单栏里的菜单项创建的Fragment;所以一个Activtiy里就包含多个Fragm
- 单例模式单例模式顾名思义就是单一的实例,涉及到一个单一的类,该类负责创建自己的对象,同时确保只有一个对象被创建,并且提供一种可以访问这个对象
- 1. 子类的构造函数如果要引用super的话,必须把super放在函数的首位class Base {Base() {System.out.p
- 一、实现原理 Mapper接口开发方法只需要程序员编写M
- 使用GroupingSearch对搜索结果进行分组Package org.apache.lucene.search.grouping Des
- java中对List分段操作的实例问题:假设A系统查询出来一个很大很大的List,现在B系统想要得到这个List来导出报表,但是B系统部署环
- 应用场景:在Android开发过程中,有时需要调用手机自身设备的功能,上篇文章主要侧重摄像头拍照功能的调用。本篇文章将综合实现拍照与视频的操
- 前言现在市面上很多应用都会有当用户按返回键的时候提示用户:再按一次将退出应用的提示,也就是双击双击返回键退出应用,接下来我们就用几种办法来实
- 本文实例讲述了Android中CountDownTimer倒计时器用法。分享给大家供大家参考,具体如下:在平时我们编程的时候,经常会用到倒计
- Android 8.0推出了PictureInPicture(画中画功能),目前只有在8.0以上的系统上支持。对比IOS,IOS的Pictu
- 在Java解析XML文件的过程中,有时需要获取符合某些特定条件的节点,以下是实现代码。import javax.xml.xpath.XPat
- 本文实例为大家分享了Android优酷圆形菜单的具体代码,供大家参考,具体内容如下先来看看效果:首先来分析一下:这个菜单可以分成三个菜单:1
- 前言本文将介绍通过Java编程在PDF文档中添加表格的方法。添加表格时,可设置表格边框、单元格对齐方式、单元格背景色、单元格合并、插入图片、