本章主要对Java中Thread类的基本方法进行学习。
1.序言
Thread类作为线程的基类,提供了一系列方法,主要有:
- Thread.sleep(long):强制线程睡眠一段时间。
- Thread.activeCount():获取当前程序中存活的线程数。
- thread.start():启动一个线程。
- Thread.currentThread():获取当前正在运行的线程。
- thread.getThreadGroup():获取线程所在线程组。
- thread.getName():获取线程的名字。
- thread.getPriority():获取线程的优先级。
- thread.setName(name):设置线程的名字。
- thread.setPriority(priority):设置线程的优先级。
- thread.isAlive():判断线程是否还存活着。
- thread.isDaemon():判断线程是否是守护线程。
- thread.setDaemon(true):将指定线程设置为守护线程。
- thread.join():在当前线程中加入指定线程,使得这个指定线程等待当前线程,并在当前线程结束前结束。
- thread.yield():使得当前线程退让出CPU资源,把CPU调度机会分配给同样线程优先级的线程。
- thread.interrupt():使得指定线程中断阻塞状态,并将阻塞标志位置为true。
- object.wai()、object.notify()、object.notifyAll():Object类提供的线程等待和线程唤醒方法。
为了便于阅读,将以上所有方法,放在5篇文章中进行学习。
本章主要学习绿色字体标记的方法,其他方法请参加其他章节。
2.线程优先级简介
直接看Thread中的源码:
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
/**
* Returns this thread's priority.
*/
public final int getPriority() {
return priority;
}
/**
* Changes the priority of this thread.
* ...
* @param newPriority priority to set this thread to
* @exception IllegalArgumentException If the priority is not in the
* range <code>MIN_PRIORITY</code> to
* <code>MAX_PRIORITY</code>.
* ...
*/
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
总结:
- java多线程的优先级范围:[1~10]
- 通过thread.setPriority(int)设置线程的优先级时,超出[1~10]的范围,会抛出一个IllegalArgumentException异常。
- 最大的线程优先级为Thread.MAX_PRIORITY = 10。
- 最小的线程优先级为Thread.MIN_PRIORITY = 1。
- 默认的线程优先级为Thread.NORM_PRIORITY = 5。
- 通过thread.getPriority()、thread.setPriority()分别能够获取和设置线程的优先级。
- 线程的优先级具有继承传递性。子线程的优先级与父线程优先级一致。
- 虽然线程优先级有10个级别,但是推荐只使用内置的三个等级。
3.实例代码与结果分析
那优先级的作用是什么呢?很多人以为:高优先级的线程会一定最先启动,高优先级的线程一定会最先运行完。
其实我也一直很困惑,然后我就查了查JDK的源码注释,看到下面一段话:
/**
* ...
* <p>
* Every thread has a priority. Threads with higher priority are
* executed in preference to threads with lower priority. Each thread
* may or may not also be marked as a daemon. When code running in
* some thread creates a new <code>Thread</code> object, the new
* thread has its priority initially set equal to the priority of the
* creating thread, and is a daemon thread if and only if the
* creating thread is a daemon.
* <p>
* ...
* @since JDK1.0
*/
public
class Thread implements Runnable {...}
这段话翻译过来就是:
- 每个线程都有一个优先级。
- 优先级较高的线程比优先级低的线程优先执行。
- 每个线程可能会与可能不会被标记为守护线程。
- 当运行中的某个线程,创建了一个新的线程时,这个新线程的初识优先级与创建它的线程一致。
综合上面的注释,我认为线程的优先级的作用如下:
- 在同一时刻,优先级高的线程优先启动。
- 在多线程运行中,优先级高的线程能得到更多的计算资源。
- 优先级高的线程并不一定最先运行结束,这取决于这个线程所处理的内容。
- 优先级低的线程并不是非要等到优先级高的线程运行完再运行,因为多线程本来指的就是线程之间的来回切换与调度。
3.1.实例代码与运行结果
下面编写代码验证一下:
线程类:
static class SleepThread extends Thread {
/**
* <p>重写Thread类的构造器,用以给线程命名</br>
* 此种方式无需定义name变量以指定线程名,因为父类Thread中已有。</p>
* @author hanchao 2018/3/8 22:59
**/
public SleepThread(String name) {
super(name);
}
/**
* <p>业务代码写在run()方法中,此方法无返回值</p>
* @author hanchao 2018/3/8 22:55
**/
@Override
public void run(){
Integer interval = RandomUtils.nextInt(100000,2000000);
System.out.println(System.nanoTime() + " : 线程[" + super.getName() + "]正在运行,预计运行" + interval + "...");
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(System.nanoTime() + " : 线程[" + super.getName() + "]运行结束");
}
}
}
测试方法:
int num = 9;
Thread[] threads = new Thread[num];
//优先级与线程的执行顺序
//注意多核CPU与单核CPU
for (int i = 0; i < num; i++) {
//部分线程设置为高级线程
if ( i < (num / 3)){
threads[i] = new SleepThread("高优先级线程-" + i);
threads[i].setPriority(Thread.MAX_PRIORITY);
}else if(i >= (num / 3) && i < (num / 3) * 2){//部分线程设置为中级线程
threads[i] = new SleepThread("中优先级线程-" + i);
threads[i].setPriority(Thread.NORM_PRIORITY);
}else{//其余线程设置为低级线程
threads[i] = new SleepThread("低优先级线程-" + i);
threads[i].setPriority(Thread.MIN_PRIORITY);
}
}
//统一运行线程
for(int i = 0; i < num; i++) {
threads[i].start();
}
System.out.println("=====================================");
结果分析:
=====================================
8848521183215 : 线程[高优先级线程-2]正在运行,预计运行327224...
8848521300084 : 线程[高优先级线程-0]正在运行,预计运行281455...
8848521177918 : 线程[高优先级线程-1]正在运行,预计运行252035...
8848522412158 : 线程[中优先级线程-4]正在运行,预计运行474837...
8848521330211 : 线程[中优先级线程-3]正在运行,预计运行442771...
8848523015705 : 线程[中优先级线程-5]正在运行,预计运行310583...
8848524692921 : 线程[低优先级线程-6]正在运行,预计运行457768...
8848530180789 : 线程[低优先级线程-7]正在运行,预计运行152680...
8848530739971 : 线程[低优先级线程-8]正在运行,预计运行338034...
9001210393973 : 线程[低优先级线程-7]运行结束
9100557081471 : 线程[高优先级线程-1]运行结束
9129977265872 : 线程[高优先级线程-0]运行结束
9159106026490 : 线程[中优先级线程-5]运行结束
9175745463719 : 线程[高优先级线程-2]运行结束
9186564876256 : 线程[低优先级线程-8]运行结束
9291293550927 : 线程[中优先级线程-3]运行结束
9306292909308 : 线程[低优先级线程-6]运行结束
9323360172767 : 线程[中优先级线程-4]运行结束
这个结果显示:高优先级的线程会优先运行,但是高优先级的线程未必会最先运行完。
3.3.其他结果
其实,在验证线程优先级作用的过程中,产生过各种各样的结果,很多结果看起来与优先级的作用相违背。产生这些结果可能的原因有:
- 运行日志无法完美展示JVM的运行过程,写日志也会消耗资源。
- 多核处理器的影响。在多核处理器上运行多线程,不仅会产生并发效果,也会产生并行效果。
- 线程进入JVM的时间不同。JVM并不是在同一时刻启动所有的线程,线程本身的启动也有先后顺序。
4.总结
关于多线程优先级的作用,我总认为自己理解不够透彻。如果谁有更好的见解,请多指教,谢谢!