【搞懂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啊之类的。但是要清楚,无论程序员怎么进行调度,也都无法实现线程的精确控制。当时良好的线程调度可以提高程序运行的效率。最后我们讲到守护线程,也就是后台线程,这里需要注意的事,虚拟机检测到没有用户线程运行了的话,不管有没有后台线程,都会退出。嗯,就这么多了,小伙伴们,都掌握了吗?下面一篇文章,我们要学习的是经典问题,生产者和消费者之类的线程同步问题,加油加油加油,未完待续哦~~~

时间: 2024-08-29 09:39:38

【搞懂Java多线程之二】多线程调度及守护进程的相关文章

[.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 多线程高级应用

[.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 多线程高级应用 本节要点: 上节介绍了多线程的基本使用方法和基本应用示例,本节深入介绍.NET多线程中的高级应用. 主要有在线程资源共享中的线程安全和线程冲突的解决方案:多线程同步,使用线程锁和线程通知实现线程同步. 1. ThreadStatic特性 特性:[ThreadStatic] 功能:指定静态字段在不同线程中拥有不同的值 在此之前,我们先看一个多线程的示例: 我们定义一个静态字段: static

轻松搞懂Java中的自旋锁

前言 在之前的文章<一文彻底搞懂面试中常问的各种"锁">中介绍了Java中的各种"锁",可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙去脉,那么这篇文章就先来会一会"自旋锁". 正文 出现原因 在我们的程序中,如果存在着大量的互斥同步代码,当出现高并发的时候,系统内核态就需要不断的去挂起线程和恢复线程,频繁的此类操作会对我们系统的并发性能有一定影响.同时聪明的JVM开发团队也发现,

【搞懂Java多线程之一】多线程相关概念,线程生命周期以及线程创建方法

楼主决定要好好地把Java的多线程给看一下,所以特地来写下博客,内容大部分都是摘抄自网上看到的资料或者书籍,能给出链接的我会给出链接.嗯,希望大家跟楼主一起抱团,加油加油(^ω^)也欢迎关注楼楼的个人博客主页www.anycodex.com. 1.多线程相关概念 程序:为了完成特定任务,用某种语言编写的一组指令集合. 进程:运行中的程序,系统跳读和资源分配的一个独立单位,操作系统会为每一个进程分配一段内存空间. 线程:比进程更小的执行单位,每一个进程可能有多个线程,线程需要放到一个进程中才能执行

多线程实战(二) : 银行业务调度系统

一. 项目需求: 1. 银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口. 2. 有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费.电话费之类业务的客户). 3. 异步随机生成各种类型的客户,生成各类型用户的概率比例为:VIP客户 :普通客户 :快速客户  =  1 :6 :3. 4. 客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务

java多线程总结二:后台线程(守护线程)

所谓的后台线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分.因此当所有的非后台线程结束时,程序也就终止了,同时会杀死所有后台线程.反过来说,只要有任何非后台线程(用户线程)还在运行,程序就不会终止.后台线程在不执行finally子句的情况下就会终止其run方法.后台线程创建的子线程也是后台线程. 下面是一个后台线程的示例: [java] view plaincopy <span style="font-size:16px;">

一文搞懂 Java 线程中断

在之前的一文<如何"优雅"地终止一个线程>中详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程的方法吗?答案是肯定的,它就是我们今天要分享的--线程中断. 下面的这断代码大家应该再熟悉不过了,线程休眠需要捕获或者抛出线程中断异常,也就是你在睡觉的时候突然有个人冲进来把你吵醒了. try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }

一篇文章彻底搞懂Java虚拟机

概念 虚拟机:指以软件的方式模拟具有完整硬件系统功能.运行在一个完全隔离环境中的完整计算机系统 ,是物理机的软件实现.常用的虚拟机有VMWare,Visual Box,Java Virtual Machine(Java虚拟机,简称JVM). Java虚拟机阵营:Sun HotSpot VM.BEA JRockit VM.IBM J9 VM.Azul VM.Apache Harmony.Google Dalvik VM.Microsoft JVM- 启动流程 基本架构 Java运行时编译源码(.j

几行代码搞定java生成解析二维码功能

最近公司要求扫描二维码和生成二维码的功能.而群里部分网友也提到了.我这里就写了一个demo,和大家分享.代码很简介,希望大家能够喜欢. 网友表示在网上搜索了很多,发现不是代码不全,就是jar不匹配. 我这里共享了一个zxing的2.2版本的例子,也提供了下载地址.实现代码如下: package com.herman.test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundExcep

彻底搞懂 Java 内存泄露

Java内存回收方式 Java判断对象是否可以回收使用的而是可达性分析算法. 在主流的商用程序语言中(Java和C#),都是使用可达性分析算法判断对象是否存活的.这个算法的基本思路就是通过一系列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,下图对象object5, object6, object7虽然有互相判断,但它们到GC Roo