【搞懂Java多线程之二】多线程调度及守护进程

在前一篇文章中说到,所有处在就绪状态中的线程,操作系统会选择优先级最高的优先进行调度,那么是不是优先级高的线程就一定比优先级低的线程先执行呢?线程的优先级又是怎么划分的呢?这篇文章,楼楼就要来说说这个问题啦!欢迎关注我的个人博客主页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啊之类的。但是要清楚,无论程序员怎么进行调度,也都无法实现线程的精确控制。当时良好的线程调度可以提高程序运行的效率。最后我们讲到守护线程,也就是后台线程,这里需要注意的事,虚拟机检测到没有用户线程运行了的话,不管有没有后台线程,都会退出。嗯,就这么多了,小伙伴们,都掌握了吗?下面一篇文章,我们要学习的是经典问题,生产者和消费者之类的线程同步问题,加油加油加油,未完待续哦~~~

    原文作者:爱上健身的菇凉
    原文地址: https://blog.csdn.net/XIAXIA__/article/details/44916783
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞