声明:
此内容来自小孩子的公众号“我们都是小青蛙”
是本人的学习笔记。如果感兴趣可以关注大佬公众号查看。
这里的原创只是不知道这么写微信公众号链接。。。
线程相关方法
Thread类提供了许多方法来方便我们获取线程的信息或者控制线程
获取线程ID
- long getId():系统会为每个线程自动分配一个long类型的id值,通过getId方法可以获取到这个值
System.out.println(new Thread().getId());
获取和设置线程名称
- void setName(String name):设置线程的名称
- String getName():获取线程的名称
- 通过构造方法设置线程的名称
Thread t1=new Thread("t1");
Thread t2=new Thread();
t2.setName("t2");
System.out.println("t1线程的名称是"+t1.getName());//输出结果:t1线程的名称是t1
System.out.println("t2线程的名称是"+t2.getName());//t2线程的名称是t2
获取和设置线程优先级
我们知道处理器会从就绪队列挑一个已经就绪的线程去执行,每个线程都可以有不同的优先级,优先级越高,越容易被处理器选中去执行
- void setPriority(int newPriority):设置线程优先级
java中的优先级是用一个正数表示,共有1-10十个等级,其中,有三个静态变量表示我们常用的- Thread.MIN_PRIORITY=1;
- Thread.NORM_PRIORITY=5;
- Thread.MAX_PRIORITY=10;
一般情况下,我们就用这三个变量去表示优先级就够用了~
- int getPriority():获取线程的优先级
Thread t1=new Thread("t1");
System.out.println("t1线程的优先级是"+t1.getPriority());//t1线程的优先级是5
t1.setPriority(Thread.MAX_PRIORITY);
System.out.println("t1线程的优先级是:"+t1.getPriority());//t1线程的优先级是:10
注意:线程优先级低并不意味着得不到处理器执行,只是执行的频率低一点而已。而且线程的优先级一般不用我们程序员去主动设置,所以这两个方法对我们来说基本没啥用
休眠
如果想要线程执行过程中让程序停一段时间之后再执行,这个停止一段时间也叫做休眠,就好像睡一段时间然后再醒来。可以通过sleep方法,来实现休眠:
- static void sleep(long millis) throws InterruptedException
程序在执行的毫秒数内让当前正在执行的线程休眠,也就是暂停执行
- static void sleep(long millis,int nanos)throws InterruptedException
程序在指定的毫秒数加纳秒数内让当前正在执行的线程休眠,也就是暂停执行
注意:sleep是一个静态方法,它会让当前线程暂停指定的时间。这个所谓的暂停,或者说是休眠其实只是把正在运行的线程阻塞掉,放到阻塞队列里,等指定的时间一到,再从阻塞队列里出来。这个方法有InterruptedException的异常说明,调用的时候需要catch一下
public static void main(String[] args){
System.out.println(Thread.currentThread().getName());//main
System.out.println(1);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(2);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(3);
}
每隔1s会输出一个数字。因为在mian方法中调用sleep方法,正在运行的线程是mian线程,所以这里休眠的也是mian线程
获取当前正在执行的线程
不同的线程可以共享同一个进程中的同一段内存,也就意味着不同的线程可以执行同一段代码,有时候有必要获取代码在哪个线程中执行
- static Thread currentThread():获取当前正在执行的线程对象的引用
public class Main {
public static void main(String[] args){
//main线程调用med方法
new Main().med();
//线程t1调用med方法
new Thread(new Runnable() {
@Override
public void run() {
new Main().med();
}
},"t1").start();
//线程t2调用med方法
new Thread(new Runnable() {
@Override
public void run() {
new Main().med();
}
},"t2").start();
}
public void med(){
Thread thread =Thread.currentThread();
System.out.println("当前正在执行的线程名称:"+thread.getName());
}
}
执行结果:
当前正在执行的线程名称:main
当前正在执行的线程名称:t1
当前正在执行的线程名称:t2
守护线程
线程可以被分为两种类型,一种普通线程另一种就叫守护线程,守护线程也被称为后台线程。守护线程在程序执行过程中并不是不可或缺的,它主要是为普通线程提供便利服务的,比方说java中不需要我们手动去释放某个对象的内存,它由一种传说的垃圾收集器去不停地释放程序中不需要的内存,在我们启动程序的时候系统会帮我们创建一个负责收集垃圾的线程,这个线程就是一个守护线程。如果所有的普通线程全部死掉了,那么守护线程也会跟着死掉,也就是程序退出了,比方说在所有的普通线程停止之后,这个负责收集垃圾的守护线程也会自动退出的。反过来说只要有一个普通线程活着,程序就不会退出。我们可以通过下边这些方法来判断一个线程是否是守护线程或者把一个线程设置为守护线程:
- boolean isDaemon():判断该线程是否为守护线程
- void setDaemon():将该线程标记为守护线程或者普通线程
- 注意:只有在线程未启动的时候才能设置该线程是否为守护线程,否则的话会抛出异常的。
public static void main(String[] args){
System.out.println("main线程是否为守护线程:"+Thread.currentThread().isDaemon());
Thread t1=new Thread("t1");
System.out.println("t1线程是否为守护线程:"+t1.isDaemon());
t1.setDaemon(true);
System.out.println("t1线程是否为守护线程:"+t1.isDaemon());
}
执行结果:
main线程是否为守护线程:false
t1线程是否为守护线程:false
t1线程是否为守护线程:true
注意:main线程并不是守护线程。从普通线程中生成的线程默认是普通线程,从守护线程中生成的线程默认是守护线程
public class Main {
public static void main(String[] args){
Thread t1=new Thread();
System.out.println("从普通线程mian中创建的线程t1是不是守护线程:"+t1.isDaemon());
Thread t2=new Thread(new Runnable(){
@Override
public void run() {
Thread t3=new Thread();
System.out.println("从守护线程t2中创建的线程t3是不是守护线程:"+t3.isDaemon());
}
});
t2.setDaemon(true);
t2.start();
}
}
执行结果:
应该是
从普通线程main中创建的线程t1是不是守护线程:false
从守护线程t2中创建的线程t3是不是守护线程:true
但是实际上这段代码出错,在另一篇博客中分析错误原因是什么。
让出本次处理器时间片
线程是处理器是时间片的分配单位。不同的线程排队等待处理器赏赐给一个时间片用来执行一会儿代码。如果一个线程已经获得了一个时间片正在执行,它突然不想执行了,可以放弃本次时间片的时间,让给处理器先给别的线程去分配一个时间片,而它参与下一轮的时间片分配
如果你听着有些绕的话,我可以给你举个例子。比如说我们把处理器比做皇帝,把各个线程比做妃子,各个妃子争相相让皇帝宠幸,为了公平,皇帝只能一次宠幸一个妃子5分钟。皇帝怎么选妃子有他自己的小算盘,但是每隔5分钟都会选一个去陪他。此时有一个妃子被皇帝挑中了,可是她陪了皇帝两分钟就觉得有些尿急,所以她主动说先放弃这次宠幸,先出去放放水再排队被挑选吧~所以只陪了皇帝两分钟便退出了此次宠幸,放水回来后再次加入妃子大军等待皇帝挑选。
【小孩子大佬的这段描述太形象了吧。。。】
- static voi yeild():表示放弃本次时间片时间,等待下次执行
这个yield方法只是建议处理器不要在本次时间片内继续执行本线程,最后实际怎么着还不一定。【也就是说放不放弃不看你,而是看皇帝。皇帝不让你小解,你就得憋着】。另外,yield是一个静态方法,表示让出当前线程本次时间片的时间
等待另一线程执行结束
有的时候需要等待一个线程执行完了,另一个线程才能继续执行。Thread类提供了这样的方法:
- void join():等待该线程终止才能继续执行
- void join(long millis):在指定毫秒内等待该线程终止,如果到达了指定时间就继续执行
- void join(long millis,int nanos):毫秒+纳秒
public static void main(String[] args){
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(i);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"t1");
t1.start();
Thread t2=new Thread(new Runnable() {
double d=1.2d;
@Override
public void run() {
try {
t1.join(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程t1执行完了,该t2了");
}
});
t2.start();
}
执行结果:
0
1
2
3
4
线程t1执行完了,该t2了
大家可以看到,虽然是两个线程t1、t2同时执行,其中t1的任务还输出一个数字停一秒,但是由于在t2的任务里有一行t1.join(),也就是说只有t1线程的任务完全执行完才可以继续执行t2的任务,所以等了5秒t1执行完了,才执行的t2。
public class Main {
public static void main(String[] args){
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(i);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"t1");
t1.start();
Thread t2=new Thread(new Runnable() {
double d=1.2d;
@Override
public void run() {
try {
t1.join(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程t1执行完了,该t2了");
}
});
t2.start();
}
执行结果:
0
1
线程t1执行完了,该t2了
2
3
4