在前一篇文章中说到,所有处在就绪状态中的线程,操作系统会选择优先级最高的优先进行调度,那么是不是优先级高的线程就一定比优先级低的线程先执行呢?线程的优先级又是怎么划分的呢?这篇文章,楼楼就要来说说这个问题啦!欢迎关注我的个人博客主页www.anycodex.com
1.线程的优先级
在Java中,线程优先级的范围为0-10,整数值越大,说明优先级更高。
- 几个相关的宏定义:
MAX_PRIORITY 10,最高优先级
MIN_PRIORITY 1,最低优先级
NORM_PRIORITY 5,默认优先级
- 相关的接口
setPriority(int x),设置线程的优先级
getPriority(),获取线程的优先级
既然线程之间有优先级,那是不是优先级高的就一定比优先级低的线程先执行呢?答案是不是的。只能说优先级高的线程有更大的可能性获得CPU,但这并不意味着就一定会先执行。
小伙伴们,我们下面来看一段代码的例子吧,呼呼(~ o ~)~zZ
package net.mindview.util; //第一步、实现Runnable接口 class MyThreadRunning implements Runnable { //第二步、重写run方法 public void run() { for (int i = 0; i <= 3; i++) { System.out.println(Thread.currentThread().getName()); } } } public class MyThread { public static void main(String[] args) { //第三步、实例化自定义的线程类对象 MyThreadRunning mtr1 = new MyThreadRunning(); MyThreadRunning mtr2 = new MyThreadRunning(); MyThreadRunning mtr3 = new MyThreadRunning(); //第四步、实例化一个Thread类对象并将自定义的线程类对象作为参数传入,后面一个参数为线程名 Thread thread1 = new Thread(mtr1, "Thread 1 is running"); Thread thread2 = new Thread(mtr2, "Thread 2 is running"); Thread thread3 = new Thread(mtr3, "Thread 3 is running"); thread1.setPriority(Thread.MAX_PRIORITY); thread2.setPriority(Thread.MIN_PRIORITY); thread3.setPriority(Thread.NORM_PRIORITY); //第五步、调用start方法启动线程 thread1.start(); thread2.start(); thread3.start(); } }
运行结果:
Thread 1 is running
Thread 1 is running
Thread 2 is running
Thread 1 is running
Thread 3 is running
Thread 3 is running
Thread 3 is running
Thread 3 is running
Thread 1 is running
Thread 2 is running
Thread 2 is running
Thread 2 is running
当当当,小伙伴们是不是明白了呢?优先级高的线程原来并不一定会先执行啊?只是有比较大的可能会获得CPU调度。小伙伴们,可能会问,都设定优先级了,还是不能保证线程按照我所希望的先后顺序去执行,我还有什么招呢?嗯,看完下面的内容,你就能学到一些招了,它就是我们的线程调度。
2.线程调度
关于线程调度,我们需要了解,只有良好的调度,才能发挥良好的系统性能,提高程序的执行效率。但是,再怎么好的调度,也都无法精确的控制程序的运行。通常我们采用线程调度的方法主要有sleep,yield和join等方法。下面我们一个一个看哈!
sleep()方法,sleep就如英文意思所释,睡眠。当某个程序进入睡眠状态时,就会将CPU资源交给其他线程,而当休眠时间到时,程序又自动由阻塞状态进入就绪状态,等待CPU资源的调度。sleep()参数是毫秒级的,如sleep(1000)是让线程休眠1s。调用这个方法的时候,需要捕获InterruptedException异常。看下面的代码吧
package net.mindview.util; //第一步、实现Runnable接口 class MyThreadRunning implements Runnable { //第二步、重写run方法 public void run() { for (int i = 0; i <= 3; i++) { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public class MyThread { public static void main(String[] args) { //第三步、实例化自定义的线程类对象 MyThreadRunning mtr1 = new MyThreadRunning(); MyThreadRunning mtr2 = new MyThreadRunning(); MyThreadRunning mtr3 = new MyThreadRunning(); //第四步、实例化一个Thread类对象并将自定义的线程类对象作为参数传入,后面一个参数为线程名 Thread thread1 = new Thread(mtr1, "Thread 1 is running"); Thread thread2 = new Thread(mtr2, "Thread 2 is running"); Thread thread3 = new Thread(mtr3, "Thread 3 is running"); //第五步、调用start方法启动线程 thread1.start(); thread2.start(); thread3.start(); } }
运行结果:
Thread 2 is running
Thread 3 is running
Thread 1 is running
Thread 3 is running
Thread 2 is running
Thread 1 is running
Thread 2 is running
Thread 1 is running
Thread 3 is running
Thread 3 is running
Thread 2 is running
Thread 1 is running
从运行结果中可以看出,线程无法精确的控制先后顺序。每次运行结果都有可能不一样。
- yield方法
yield方法也像英文的意思一样,就是让步。暂停当前线程,让出CPU运行时间,进入就绪状态,从而让其他线程获得CPU时间。不过对于抢占式操作系统没有什么意义,或者说没有效果。
package net.mindview.util; //第一步、实现Runnable接口 class MyThreadRunning1 implements Runnable { //第二步、重写run方法 public void run() { for (int i = 0; i <= 3; i++) { System.out.println(Thread.currentThread().getName()); } } } //第一步、实现Runnable接口 class MyThreadRunning2 implements Runnable { //第二步、重写run方法 public void run() { for (int i = 0; i <= 3; i++) { System.out.println(Thread.currentThread().getName()); Thread.yield(); } } } public class MyThread { public static void main(String[] args) { //第三步、实例化自定义的线程类对象 MyThreadRunning1 mtr1 = new MyThreadRunning1(); MyThreadRunning2 mtr2 = new MyThreadRunning2(); //第四步、实例化一个Thread类对象并将自定义的线程类对象作为参数传入,后面一个参数为线程名 Thread thread1 = new Thread(mtr1, "Thread 1 is running"); Thread thread2 = new Thread(mtr2, "Thread 2 is running"); //第五步、调用start方法启动线程 thread1.start(); thread2.start(); } }
运行效果: 如果不加yield的可能的运行结果:
Thread 1 is running Thread 1 is running
Thread 2 is running Thread 2 is running
Thread 1 is running Thread 1 is running
Thread 2 is running Thread 1 is running
Thread 1 is running Thread 1 is running
Thread 2 is running Thread 2 is running
Thread 1 is running Thread 2 is running
Thread 2 is running Thread 2 is running
从运行效果中可以看出,线程2每执行一次就会让出CPU资源一次,但也只是可能的运行结果。
- join方法
join方法的作用可能从英文意思上理解不是那么简单,那其实呢,join方法的作用是想让一个线程等待另一个线程执行完毕后再继续执行。可以添加参数,long类型的毫秒,等待该进程终止最长为多少秒。如thread.mt()就是等thread线程执行完之后,再执行其他的程序。我们可以看下面的代码:
package net.mindview.util; //第一步、继承Thread类 class MyThreadRunning extends Thread { //构造函数 public MyThreadRunning() { super("My Thread"); } //第二步、重写run方法 public void run() { for (int i = 0; i <= 3; i++) { System.out.println("新建一个线程" + getName()+i); try { sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public class MyThread { public static void main(String[] args) { //第三步、实例化自定义的线程类对象 MyThreadRunning mtr = new MyThreadRunning(); //第四步、调用start方法从而启动run方法 mtr.start(); try { mtr.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("mtr你执行完了是吧?我是主线程,我要来打印了。"); } }
运行结果:
新建一个线程My Thread0
新建一个线程My Thread1
新建一个线程My Thread2
新建一个线程My Thread3
mtr你执行完了是吧?我是主线程,我要来打印了。
3.守护进程
守护进程也叫后台进程,是一种为其他线程提供服务的一种线程。当虚拟机检测到没有用户进程执行了的话,即使还有后台进程,JVM也有可能会退出的。
相关接口:
setDaemon(boolean)将一个线程设置为后台进程;
Thread.isDaemon()判断一个线程是否为后台进程。
我们可以来看下面的代码:
package net.mindview.util; //第一步、继承Thread类 public class MyThread { public static void main(String[] args) { Thread t1 = new MyCommon(); Thread t2 = new Thread(new MyDaemon()); t2.setDaemon(true); //设置为守护线程 t2.start(); t1.start(); } } class MyCommon extends Thread { public void run() { for (int i = 0; i < 5; i++) { System.out.println("线程1第" + i + "次执行!"); try { Thread.sleep(7); } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyDaemon implements Runnable { public void run() { for (long i = 0; i < 9999999L; i++) { System.out.println("后台线程第" + i + "次执行!"); try { Thread.sleep(7); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果:
线程1第0次执行!
后台线程第0次执行!
后台线程第1次执行!
线程1第1次执行!
线程1第2次执行!
后台线程第2次执行!
线程1第3次执行!
后台线程第3次执行!
线程1第4次执行!
后台线程第4次执行!
后台线程第5次执行!
同学们,由运行结果中是不是可以看出,后台线程还没有执行完呢,程序就退出了。
以上就是我们关于Java多线程的第二部分内容了,总结一下,这篇文章主要讲Java多线程的调度问题。调度方法主要有睡眠啊,让步啊,以及join啊之类的。但是要清楚,无论程序员怎么进行调度,也都无法实现线程的精确控制。当时良好的线程调度可以提高程序运行的效率。最后我们讲到守护线程,也就是后台线程,这里需要注意的事,虚拟机检测到没有用户线程运行了的话,不管有没有后台线程,都会退出。嗯,就这么多了,小伙伴们,都掌握了吗?下面一篇文章,我们要学习的是经典问题,生产者和消费者之类的线程同步问题,加油加油加油,未完待续哦~~~