专栏首页做不甩锅的后端多线程基础(十二):Thread优先级分析

多线程基础(十二):Thread优先级分析

1.thread中的优先级

在前面学习Thread源码的时候,提到了Thread可以设置优先级。其优先级通过setPriority方法进行设置。

/**
 * 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;

在Thread中,提供了3种优先级类型。实际上java提供的线程优先级为1-10的整数。按照传统的理解,如果设置为高优先级,那么其线程在执行的过程中得到的CPU执行的机会将大于优先级低的线程。那么下面我们对线程的优先级进行验证。

2.验证

import java.util.concurrent.TimeUnit;

public class PriorityTest extends Thread{

	private volatile long count = 0;

	public long getCount() {
		return count;
	}

	@Override
	public void run() {
		try {
			while (true) {
				count ++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {

		PriorityTest t1 = new PriorityTest();
		PriorityTest t2 = new PriorityTest();
		PriorityTest t3 = new PriorityTest();
		PriorityTest t4 = new PriorityTest();
		t1.setPriority(1);
		t2.setPriority(10);
		t3.setPriority(10);
		t4.setPriority(10);
		t1.start();
		t2.start();
		t3.start();
		t4.start();


		new Thread(() -> {
			while (true) {
				try {
					TimeUnit.SECONDS.sleep(1);
					System.out.println("print:t1["+t1.getCount()+ "] t2["+ t2.getCount()+"] t3["+ t3.getCount()+"] t4["+ t4.getCount()+"]");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();


	}

}

上述代码,在windows系统idea测试输出:

print:t1[141315520] t2[149400119] t3[161544594] t4[153872496]
print:t1[259967363] t2[289447885] t3[292740550] t4[302513889]
print:t1[437426834] t2[481490591] t3[488735422] t4[495629048]
print:t1[637082266] t2[689029535] t3[701732100] t4[703677802]
print:t1[819953148] t2[890855295] t3[905247104] t4[912506447]
print:t1[1018990612] t2[1091062394] t3[1113091363] t4[1119786453]
print:t1[1205206535] t2[1272513475] t3[1294425273] t4[1304756799]
print:t1[1404866545] t2[1470620675] t3[1492622623] t4[1496612016]
print:t1[1590767926] t2[1651400185] t3[1688239699] t4[1699097044]
print:t1[1773328378] t2[1847804448] t3[1883739945] t4[1905774630]
print:t1[1971298149] t2[2041965328] t3[2081918834] t4[2104698757]
print:t1[2157880017] t2[2232498558] t3[2269818261] t4[2306271184]
print:t1[2330479610] t2[2409405215] t3[2452668020] t4[2489070628]
print:t1[2469966402] t2[2558469732] t3[2606285673] t4[2626978898]

在windows系统上可以看出,优先级低的线程会比高优先级的线程获得的执行次数少。那么我们将这个代码放到linux上执行。

print:t1[100414879] t2[99694288] t3[95904584] t4[37197108]
print:t1[171295255] t2[171298328] t3[170851240] t4[64810891]
print:t1[246907489] t2[246964402] t3[243168325] t4[93703194]
print:t1[318778259] t2[320983272] t3[320230850] t4[122450830]
print:t1[397315080] t2[393067515] t3[394953718] t4[151256012]
print:t1[469708806] t2[467779539] t3[469846211] t4[180071710]
print:t1[544945482] t2[542967801] t3[544680871] t4[208462144]
print:t1[614797793] t2[612902001] t3[614233757] t4[236198233]
print:t1[690537087] t2[686338172] t3[684265213] t4[264657155]
print:t1[760772881] t2[759907861] t3[757426387] t4[293023592]
print:t1[834746175] t2[830694617] t3[830638526] t4[321241117]
print:t1[906519160] t2[902894713] t3[902126475] t4[348491969]
print:t1[975998822] t2[976049732] t3[974747744] t4[376682800]
print:t1[1051943748] t2[1046739616] t3[1044848610] t4[405288434]
print:t1[1123128682] t2[1118649942] t3[1116233253] t4[433625044]
print:t1[1189535190] t2[1185455433] t3[1183875916] t4[459984507]
print:t1[1264878289] t2[1260889713] t3[1258093372] t4[488926353]

那么可以看出,t1由于先启动,其执行次数居然比后续优先级高的t2、t3、t4次数多很多。这个线程优先级的策略,在这个地方就失效了。

3.原因分析

实际上,这涉及到了java的线程调度。我们在前面学习的过程中知道,java线程实际上就是操作系统中的线程。这个关系是一一对应的。一个java线程实际上就是操作系统中的线程。那么线程调度,实际上就是由操作系统进行控制的。jvm本身的线程优先级,只是一个建议性的结果。VM规范中规定每个线程都有优先级,且优先级越高越优先执行,但优先级高并不代表能独自占用执行时间片,可能是优先级高得到越多的执行时间片,反之,优先级低的分到的执行时间少但不会分配不到执行时间。JVM的规范没有严格地给调度策略定义。 而线程调度的模式分为两种——抢占式调度和协同式调度。 抢占式调度指的是每条线程执行的时间、线程的切换都由系统控制,线程的切换不由线程本身决定,系统控制指的是在系统某种运行机制下,可能每条线程都分同样的执行时间片,也可能是某些线程执行的时间片较长,甚至某些线程得不到执行的时间片。在这种机制下,一个线程的阻塞不会导致整个进程阻塞。 协同式调度指某一线程执行完后主动通知系统切换到另一线程上执行,线程的执行时间由线程本身控制,这种模式就像接力赛一样,一个人跑完自己的路程就把接力棒交接给下一个人,下个人继续往下跑。线程的执行时间由线程本身控制,线程切换可以预知,不存在多线程同步问题,但它有一个致命弱点:如果一个线程编写有问题,运行到一半就一直阻塞,那么可能导致整个系统阻塞挂起。 参考网上的一张图:

因此,java实际上就是使用的是抢占式线程调度。需要注意的是,我们在前面聊中断机制的时候,java是使用的是协调式中断。这与线程调度有很大的不同。 那么由此可以知道,java的线程优先级,实际上取决于操作系统如何进行线程调度。如果操作系统采用了线程的优先级状态然后来调度,那么这个优先级就是生效的,反之则不一定有用。

4.总结

通过本文,我们可以知道,java中的线程优先级,实际上取决于操作系统是如何实现的。这存在很大的不确定性。因此我们在编码的过程中,不要依赖于这个优先级进行编程。否则可能在某些操作系统中就会导致失效。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 什么?面试官让我用ArrayList实现一个阻塞队列?

    在准备开始详细分析java多线程源码时,发现了这样一个问题,也就是说,在面试的时候经常要求,手写阻塞队列,或者手写一个程序,交替打印1-10000。于是,看到这...

    冬天里的懒猫
  • java线程池(三):ThreadPoolExecutor源码分析

    在前面分析了Executors工厂方法类之后,我们来看看AbstractExecutorService的最主要的一种实现类,ThreadpoolExecutor...

    冬天里的懒猫
  • java线程池(一):java线程池基本使用及Executors

    在前面学习线程组的时候就提到过线程池。实际上线程组在我们的日常工作中已经不太会用到,但是线程池恰恰相反,是我们日常工作中必不可少的工具之一。现在开始对线...

    冬天里的懒猫
  • 一个例子讲Synchronized关键字

    文中的例子从线程调度上讲不够严谨。原因是没输出,并不代表线程无法进入方法,有可能是线程一直没有被调度。

    小金狗子
  • Android学习笔记(十四)方便实用的首选项-PreferenceActivity

     突然发现已经好多天没更新博客了,最近公司项目正在进行一个大跨度的重构,又碰上有新需求,一连好多天都是很晚才到家。其实这篇博文在草稿箱里面也存了很久了,本来想着...

    codingblock
  • 记录一个try catch没有生效的原因,以便备忘

    为了测试方便,直接建立的MFC对话框工程,直接选择Release编译。在代码中测试发现,添加了try catch之后,运行程序出现异常时仍然报错,而没有catc...

    大菊观
  • [UWP 自定义控件]了解模板化控件(3):实现HeaderedContentControl

    是不是觉得它们中出了一个叛徒?这个示例中除了ListBox控件其它都自带Header,但是ListBox没有Header属性,只好用一个TextBlock模仿它...

    dino.c
  • 深入理解java异常处理机制

    http://blog.csdn.net/hguisu/article/details/6155636

    bear_fish
  • 深入理解java异常处理机制

     1. 引子        try…catch…finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解。不过,...

    大闲人柴毛毛
  • DedeCMS V5.7sp2网站漏洞如何修复

    织梦dedecms,在整个互联网中许多企业网站,个人网站,优化网站都在使用dede作为整个网站的开发架构,dedecms采用php+mysql数据库的架构来承载...

    技术分享达人

扫码关注云+社区

领取腾讯云代金券