线程相关方法一
声明
版权声明:本文参考CSDN博主「一个快乐的野指针~」的原创文章,仅用作个人学习,特此声明
原文链接: https://blog.csdn.net/qq_44715943/article/details/116714584
线程方法
1. 获取方法
1、方法概述
获取当前线程对象、获取线程对象名字、修改线程对象名字
方法名 | 作用 |
---|---|
static Thread currentThread() | 获取当前线程对象 |
String getName() | 获取线程对象名字 |
void setName(String name) | 修改线程对象名字 |
需要注意的是,当没有设置线程名称时,默认名称为
- Thread-0
- Thread-1
- Thread-2
- Thread-3
- …
2、eg.
/**
* 线程获取方法测试
*/
public class Demo {
public static void main(String[] args) {
Thread t = new Thread(new ThreadDemo());
//t.run();
// 启动线程
t.start();
}
}
class ThreadDemo extends Thread {
public void run(){
for(int i = 0; i < 2; i++){
// currentThread就是当前线程对象。当前线程是谁呢?
// 当t1线程执行run方法,那么这个当前线程就是t1
// 当t2线程执行run方法,那么这个当前线程就是t2
Thread currentThread = Thread.currentThread();
//currentThread.setName("分支线程1");
System.out.println(currentThread.getName() + "-->" + i);
System.out.println(super.getName() + "-->" + i);
System.out.println(this.getName() + "-->" + i);
}
}
}
很明显直接调用run方法时当前线程为main线程
当用线程start方法调用run方法时,当前线程为分线程,其名称被主动修改为"分支线程1"
3、发现了一个问题(待解决)
只有一个分支线程用于输出0,1.为什么该线程名称为Thread-1而不是Thread-0呢?
2. sleep相关方法
1、sleep方法
方法名 | 作用 |
---|---|
static void sleep(long millis) | 让当前线程休眠millis毫秒 |
- 静态方法:Thread.sleep(1000);
- 参数是毫秒
- 作用: 让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用。
这行代码出现在A线程中,A线程就会进入休眠。
这行代码出现在B线程中,B线程就会进入休眠。 - Thread.sleep()方法,可以做到这种效果:
间隔特定的时间,去执行一段特定的代码,每隔多久执行一次。
/**
* 用sleep方法实现每间隔1s时间打印一个数字
*/
public class SleepTest extends Thread{
public static void main(String[] args) {
//每间隔1s时间打印一个数字
for(int i = 0; i < 2; i++){
System.out.println(Thread.currentThread().getName() + "--->" + i);
// 睡眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2、中断sleep的方法
方法名 | 作用 |
---|---|
void interrupt() | 终止线程的睡眠 |
![]() |
/**
* 打断sleep方法测试
*/
public class InterruptSleepTest {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable2());
t.setName("t");
t.start();
//线程休眠5s以后主动打断休眠状态
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。)
t.interrupt();
}
}
class MyRunnable2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---> begin");
try {
// 睡眠1年
Thread.sleep(1000 * 60 * 60 * 24 * 365);
} catch (InterruptedException e) {
//e.printStackTrace();
}
//1年之后才会执行这里
System.out.println(Thread.currentThread().getName() + "---> end");
}
}
分析上测试代码可知,本来run方法是要线程睡眠一年的时间才会输出"当前线程名称---> end"
但main方法中主动用interrupt();
方法打断了线程的睡眠
需要注意的是这种终断睡眠的方式依靠了java的异常处理机制
3. run方法的一个小知识点
为什么run()方法只能try…catch…不能throws?
因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常。
4. stop方法强行终止线程(不推荐)
1、eg.
/**
* 终止程序方法:stop方法测试
*/
public class StopTest {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable3());
t.setName("t");
t.start();
//模拟5秒:因为要在5s以后再强行终止t线程,所以要用sleep方法将stop方法延后5s
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 5秒之后强行终止t线程
t.stop(); // 已过时(不建议使用。)
}
}
class MyRunnable3 implements Runnable {
@Override
public void run() {
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() + "--->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2、需要注意的是
- 要在start方法启动线程从而调用run方法之后先休眠5s再调用stop方法。
因为start方法只是一个启动方法,它的执行和结束是一瞬间的事情,不会等待run方法运行完成后再结束
如果不将stop方法延后5s,它就会在start方法执行之后瞬间执行,run方法还没开始就被强行终止了。如下图,运行结果为空,很明显run方法没来得及执行
如下图所示:在start方法和stop方法之间加入延后代码之后,实现了5秒之后强行终止t线程的目的,run方法只执行了5s也就是只输出了0~4就被强行终止了
2. 这种方式存在很大的缺点:容易丢失数据。因为这种方式是直接将线程杀死了,线程没有保存的数据将会丢失。不建议使用。
那么,应该通过一个什么样的方法来合理地终止线程的执行呢?
引出以下内容
Java中合理结束一个进程的执行(常用)
1、eg.
/**
* 用合理的方法实现结束进程执行:加判断
*/
public class RationalMethodTest {
public static void main(String[] args) {
MyRunable r = new MyRunable();
Thread t = new Thread(r);
t.setName("t");
t.start();
// 模拟5秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 终止线程
// 你想要什么时候终止t的执行,那么你把标记修改为false,就结束了。
r.run = false;
}
}
class MyRunable implements Runnable {
boolean run = true;
@Override
public void run() {
for (int i = 0; i < 10; i++){
if(run){
System.out.println(Thread.currentThread().getName() + "--->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
// return就结束了,结束前未保存的数据在这一步保存,防止数据丢失
return;
}
}
}
}
需要注意:
2、为什么if()语句要在循环里面?
由于一个线程一直运行此程序,要是if判断在外面只会在启动线程时判断并不会结束,因此需要每次循环判断一下标记。
补充知识:线程调度
常见线程调度模型
-
抢占式调度模型:
优先级比较高的线程,其抢到的CPU时间片的概率就高一些/多一些。
java采用的就是抢占式调度模型。 -
均分式调度模型:
平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
平均分配,一切平等。
有一些编程语言,线程调度模型采用的是这种方式。
5. 线程调度相关方法
1、获取和设置优先级
获取、设置线程优先级
方法名 | 作用 |
---|---|
int getPriority() | 获得线程优先级 |
void setPriority(int newPriority) | 设置线程优先级 |
- 最低优先级1
- 默认优先级是5
- 最高优先级10
优先级比较高的获取CPU时间片可能会多一些。
常量名 | 备注 |
---|---|
static int MAX_PRIORITY | 最高优先级(10) |
static int MIN_PRIORITY | 最低优先级(1) |
static int NORM_PRIORITY | 默认优先级(5) |
注意:main线程的默认优先级是:5
eg.
/**
* 优先级测试
*/
public class PriorityTest extends Thread{
public static void main(String[] args) {
System.out.println("最高优先级为:" + Thread.MAX_PRIORITY);//最高优先级:10
System.out.println("最低优先级为:" + Thread.MIN_PRIORITY);//最低优先级:1
System.out.println("默认优先级为:" + Thread.NORM_PRIORITY);//默认优先级:5
// main线程的默认优先级是:5
System.out.println(currentThread().getName() + "线程的默认优先级是:" + currentThread().getPriority());
Thread thread = new Thread(new MyRunnable());
thread.setPriority(10);
thread.start();
//thread.join();
// 优先级较高的,只是抢到的CPU时间片相对多一些。
// 大概率方向更偏向于优先级比较高的。
for(int i = 0; i < 2; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for(int i = 0; i < 2; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
发现了一个问题:为什么优先级低的main反而每次都能抢先执行?
- 其实,即使设置了线程的优先级,一样无法确保这个线程一定先执行,因为它有很大的随机性。它并无法控制执行哪个线程,因为线程的执行,是抢占资源后才能执行的操作,而抢点资源时,最多是给于线程优先级较高的线程一点机会而已,能不能抓住可是不一定的
- 父线程main先启动占了CPU。如果要父线程后执行,那就在start()后加个join(),如下图,此时并入的支线先执行,再执行当前进程main
2、让位方法yield
1、概述
方法名 | 作用 |
---|---|
static void yield() | 让位方法,当前线程暂停,回到就绪状态,让给其它线程。 |
yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。
yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。
注意:在回到就绪之后,有可能还会再次抢到CPU时间片。
2、eg.
/**
* 让位方法yield测试
*/
public class YieldTest {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable1());
t.setName("t");
t.start();
for(int i = 1; i <= 1000; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
class MyRunnable1 implements Runnable {
@Override
public void run() {
for(int i = 1; i <= 1000; i++) {
//让位
if(i % 100 == 0){
Thread.yield(); // 当前线程暂停一下,让给主线程。
}
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
3、观察结果,发现了很多细节
- 无论优先级的高低,一定是main先执行
因为进程先抢占CPU,所以观察上图发现,当start方法调用了线程的run方法时,main方法已经运行了一段时间
当t线程run开始执行,main方法暂停
- 某个线程的让位方法执行以后,不一定是立马执行其他线程
因为yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。此时的让位线程是可以继续抢夺CPU时间片的。观察上图发现,很明显主线程main还没有执行完毕,CPU时间片又被之前的让位线程抢到了,让位线程继续执行直到下一次调用让位方法才会再次让位给主线程执行,如下图所示
- 以上过程循环直到某一线程执行完毕打破该循环
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律