一篇搞懂Java多线程运行机制

这篇具有很好参考价值的文章主要介绍了一篇搞懂Java多线程运行机制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一篇搞懂Java多线程运行机制

前言

Java是一种支持多线程编程的语言。多线程可以让程序同时执行多个任务,从而提高程序的效率和响应速度。在本篇博客中,我将介绍Java多线程的基础知识,包括线程的创建、启动、中断以及线程同步等方面。

什么是程序?
程序是为完成特定任务,用某种语言编程写的一组指令的集合。一组计算机能识别和执行的指令,运行于电子计算机上,满足人们某种需求的信息化工具(简单来说就是我们写的代码)

什么是进程?
进程是指运行中的程序,比如我们使用的QQ,启动qq.exe可执行程序就启动了一个线程,操作系统就会为进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。

进程是程序的一次执行过程,或正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程。具体关系如图所示:
一篇搞懂Java多线程运行机制

打开电脑的任务管理器,即可查看电脑运行的进程
一篇搞懂Java多线程运行机制
进程共有三种状态:就绪、阻塞和运行

  • 就绪态:就绪状态是指程序已达到可以运行的状态,只等CPU分配资源就可以运行的状态。
  • 阻塞态:当程序运行条件没有满足时,需要等待条件满足时候才能执行时所处的状态,如等待i/o操作时候,此刻的状态就叫阻塞态。
  • 运行态:进程占用CPU,并在CPU上运行。即程序正在运行时所处的状态。

线程

线程是由进程创建的,是一个进程的实体,一个进程可以有多个线程。比如:百度网盘的下载任务,一个下载任务对应一个线程,当同时下载多个任务时,相当于开启了多个线程,线程分为单线程和多线程。

  • 单线程:同一时刻,只允许执行一个线程
  • 多线程:同一时刻,可以执行多个线程

在Java中线程使用有两种方式

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,重写run方法

并发和并行

早期计算机的 CPU 都是单核的,一个 CPU 在同一时间只能执行一个进程/线程,当系统中有多个进程/线程等待执行时,CPU 只能执行完一个再执行下一个。并发和并行是用来操作线程和进程的方式。

并发
同一时刻,多个任务交替执行,造成一种同时的错觉,并不是正在同时运行。(单核CPU实现的多任务就是并发)
一篇搞懂Java多线程运行机制
并行
同一时刻,多个任务同时执行,多核CPU可以实现并行。
一篇搞懂Java多线程运行机制

注意:也可能会有并发和并行同时存在


Runnable接口

一个实现Runnable接口的类就是一个线程,通过Thread类启动。需要把实现类对象传入Thread类的构造方法中 然后通过Thread的start方法启动该Runnable实现类的线程。(因为Java是单继承机制,所以当有某个类已经继承了某个父类时,则需要用到Runnable接口)

该接口中只有一个**run()**方法,所以需要传入Thread类的构造器中进行启动

Thread类

Java语言是支持多线程的,一个正在运行的Java程序可以称之为一个进程(process),在每个进程里面包含多个线程.一个Thread实例对象,就是一个线程。Thread类继承于Runnable接口
一篇搞懂Java多线程运行机制
常用方法

  • getName():获取当前线程的名称,默认线程名称是Thread-索引
  • setName(String name):将此线程的名称更改为指定的名称,通过构造器也可以设置线程名称
  • sleep(long time):让当前线程休眠指定的时间后再继续执行,单位是毫秒。
  • run():线程任务方法(线程需要执行的业务内容)
  • start():开启当前线程
  • currentThread():获取当前执行线程的引用对象
  • setPriority(int newPriority) :更改线程的优先级
  • getPriority():获取线程的优先级
  • interrupt():中断线程
  • yield():给调度程序的一个提示,当前线程愿意得到它当前的处理器的使用(线程的礼让)
  • join():线程的插队

用来控制线程优先级的范围常用的有三种:

MAX_PRIORITY 线程可以拥有的最大优先级(10)
MIN_PRIORITY 线程可以拥有的最小优先级(1)
NORM_PRIORITY 被分配给线程的默认优先级(5)

注意:Thread类本身没用run方法的,而是继承Runnable接口重写得来的

使用start方法,会自动开启子线程,调用run方法。这里使用了设计模式(代理模式

线程的创建

在Java中,可以通过继承Thread类或实现Runnable接口来创建一个线程。继承Thread类需要重写run()方法,而实现Runnable接口需要实现run()方法。下面是两种创建线程的示例代码:

  • 继承Thread类的方式:
public class MyThread extends Thread {
    @Override
    public void run() {
        // 线程执行的代码
    }
}

MyThread myThread = new MyThread();
myThread.start(); // 启动线程
  • 实现Runnable接口的方式:
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程执行的代码
    }
}

MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start(); // 启动线程

原理和注意事项

当执行执行代码程序时(run),会开启一个进程。之后会进入到主方法main中,会开启一个主线程(main线程)
一篇搞懂Java多线程运行机制

可在控制台使用 jconsole指令监控线程执行情况,如果出现 不是内部或外部命令,也不是可运行的程序或批处理文件 。则需要检查jdk环境配置(建议手动配置)

注意:执行时需要开启需要监控的线程,才能进行查看

  • 当主线程执行完毕后,子线程未执行完,则继续执行,执行完毕所有线程后,程序进程退出
  • 当程序进程结束,所有线程直接结束

为什么不直接调用run方法?

xx.run(); xx.start();

为什么不在主方法中直接调用run,而是通过start方法开启线程

如果使用xx.run()方法,则是主线程直接调用run方法,而此时线程的名称是main(此时的run方法就是一个普通的方法,并没有真正的启动子线程,会阻塞这里,等待run方法执行完毕,才会继续执行main主方法中代码);而使用xx.start()方法,则是通过子线程调用run方法(底层是JVM调用的,相当于开启了一个子线程)

start方法调用后,该线程并不一定会立刻马上执行,只是将线程变成了可运行状态。具体什么时候执行,取决于CPU,由CPU统一调度。

一篇搞懂Java多线程运行机制


线程终止和中断

线程终止的两种情况:

  1. 当线程完成任务后,会自动退出
  2. 通过使用变量来控制run方法退出的方式停止线程,即通知方式

中断
interrupt方法,用来中断线程,但并没有真正的结束线程,所以一般用于中断正在休眠的线程,此时调用该线程的interrupt方法,那么该线程将抛出一个InterruptedException中断异常,从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException。

如果需要中断一个线程,可以调用线程的interrupt()方法。但是,中断线程只是给线程一个中断的标志位,并不能直接终止线程的执行。线程需要自己在执行的过程中检查这个标志位,并在合适的时候终止自己的执行。下面是一个简单的中断线程的示例代码:

Thread thread = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        // 线程执行的代码
    }
});

thread.start(); // 启动线程

// 中断线程
thread.interrupt();

用户线程和守护线程

Java的线程可以分为两类:User Thread(用户线程)、Daemon Thread(守护线程)

  • 用户线程:也叫工作线程,当线程的任务执行完毕或以通知的方式来结束,平时用到的普通线程均是用户线程,当在Java程序中创建一个线程,它就被称为用户线程
  • 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。所以当系统只剩下守护进程的时候,Java虚拟机会自动退出。

如何将一个线程设置为守护线程?
需要使用Thread类中的setDaemon方法

  • setDaemon(boolean on):为true是守护线程,反则为false,线程默认是用户线程(此方法必须在开始线程之前调用)

当主线程结束,子线程自动结束,将子线程设置为守护线程,演示如下文章来源地址https://www.toymoban.com/news/detail-414536.html

public class ThreadTest3 {
    public static void main(String[] args) {
        DaemonThread daemonThread = new DaemonThread();
        daemonThread.setDaemon(true);
        daemonThread.start();
        for (int i = 1; i < 10; i++) {
            System.out.println("主线程(用户线程)");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class DaemonThread extends Thread {
    @Override
    public void run() {
        for (; ; ) {//无线循环
            System.out.println("子线程(设置为守护线程)执行中");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

代码演示

  1. 使用Thread使用线程,每隔一秒输出hello,worldI(死循环)
public class Thread01 {
    public static void main(String[] args) {
        test t1 = new test();
        t1.start();
    }
}

class test extends Thread{
    @Override
    public void run() {
        while (true){
        	System.out.println(Thread.currentThread().getName());//获取线程的名称
            System.out.println("hello,world");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 实现Runnable接口,使用线程
public class ThreadTest {
    public static void main(String[] args) {
        Test test = new Test();
        Thread thread = new Thread(test);
        thread.start();
    }
}

class Test implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println(Thread.currentThread().getName());//获取线程的名称
            System.out.println("hello,world");
            try {
                Thread.sleep(1000);//休眠一秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 多线程的使用:创建两个线程,一个线程每隔1秒输出hello,world,输出10次,退出。一个线程每隔1秒输出hi,输出5次退出
public class ThreadTest1 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new T1());
        thread1.start();
        Thread thread2 = new Thread(new T2());
        thread2.start();
    }
}

class T1 implements Runnable {
    int count = 0;

    @Override
    public void run() {
        //每隔1秒输出hello,world,输出10次
        while (true) {
            if (count == 10) {
                break;
            }
            System.out.println(Thread.currentThread().getName());
            System.out.println("hello,world " + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class T2 implements Runnable {
    int count = 0;

    @Override
    public void run() {
        //每隔1秒输出hello,world,输出10次
        while (true) {
            if (count == 5) {
                break;
            }
            System.out.println(Thread.currentThread().getName());
            System.out.println("hi " + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 主线程输出5次hello之后,会让子线程提前结束休眠(原版子线程会休眠20秒,interrupt之后会提前结束休眠)
public class ThreadTest1 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.setPriority(Thread.MIN_PRIORITY);
        t.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("hello");
            Thread.sleep(1000);
        }
        t.interrupt();
    }
}

class T extends Thread {
    int count = 0;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "线程执行中..." + (++count));
        }
        try {
            System.out.println("线程休眠中...");
            Thread.sleep(20000);//20秒
        } catch (InterruptedException e) {//当执行interrupt方法,InterruptedException会捕获到中断异常
            System.out.println(Thread.currentThread().getName() + "被中断线程(interrupt)了");
        }
    }
}
  1. 线程插队:让主线程运行输出5次之后,等待子线程执行完毕之后,主线程继续执行
public class ThreadTest2 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("主线程" + Thread.currentThread().getName() + "运行中...");
            Thread.sleep(100);
            if (i == 5) {
                t.join();//子线程插队(强制成功)
//                Thread.yield();//子线程插队(不一定成功)
            }
        }
    }
}

class T extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("子线程" + Thread.currentThread().getName() + "运行中...");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

到了这里,关于一篇搞懂Java多线程运行机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何理解场外期权交易?个股期权一篇搞懂

    场外期权是一种金融衍生品,指在非集中性的交易场所进行的非标准化的金融期权合约。它是一种买卖双方达成的合约,赋予买方在未来的某一特定日期以特定价格购买或出售一种资产的权利,但不必承担必须履行的义务。下文科普如何理解场外期权交易?个股期权一篇搞懂

    2024年04月24日
    浏览(41)
  • 什么是双向链表,一篇搞懂双向链表

    还不清楚单向链表的同学可以去看我另一篇文章,实践总结:一篇搞懂链表——单链表和双指针技巧 首先,我们先看下双向链表(下文用双链表表述)的图示,见下图: 与单链表不同的是,双链表有两个方向,对应单链表节点中的一个引用字段next,双链表每个节点中都含有

    2024年03月13日
    浏览(32)
  • 一篇搞懂数学在OpenGL中的应用及矩阵

    目录 一、图形学中的矩阵 1.矩阵的计算公式 2.矩阵变换 3.为什么旋转,平移都是左乘矩阵,不能右乘 4.齐次坐标系统 5.变换先后顺序 二、利用矩阵来变换图形 (补充) 三、OpenGL中的三种变换矩阵  话不多说,我把我看的视频链接贴出来,下面的笔记是由视频学习和自己的补

    2024年02月03日
    浏览(24)
  • 基于IIC通信的显示器OLED编程详解(一篇搞懂)

    上一篇博客介绍了IIC通信,这篇我们就来玩玩oled模块。当然选用的是IIC接口,因为市面上还有一种是SPI接口的。对于oled长啥样,采用了什么材料,工艺怎么怎么样等等这里就不作任何介绍,搞得眼花缭乱的,对我们用它做开发也没任何帮助,同时节省读者阅读时间。为什么

    2024年02月09日
    浏览(39)
  • 探索Java并发编程利器:LockSupport,一种高效的线程阻塞与唤醒机制

    关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 我们继续总结学习 Java基础知识 ,温故知新。 LockSupport 是 Java SE 9 及以上版本中引入的一个线程同步工具类,用

    2024年02月16日
    浏览(42)
  • 【JavaSE专栏84】线程让步,一种线程调度的机制

    作者主页 :Designer 小郑 作者简介 :3年JAVA全栈开发经验,专注JAVA技术、系统定制、远程指导,致力于企业数字化转型,CSDN学院、蓝桥云课认证讲师。 主打方向 :Vue、SpringBoot、微信小程序 本文讲解了 Java 中线程让步的语法和应用场景,并给出了样例代码。线程让步是一种

    2024年02月11日
    浏览(22)
  • 一篇搞明白微信小程序的基本授权功能

    一、介绍         由于部分接口需要经过用户授权同意才能调用。我们把这些接口按使用范围分成多个  scope  , 用户选择对  scope  来进行授权,当授权给一个  scope  之后,其对应的所有接口都可以直接使用。 此类接口调用时: 如果用户未接受或拒绝过此权限,会弹窗

    2024年01月17日
    浏览(42)
  • eclipse进入断点之后,一直卡死,线程一直在运行【记录一种情况】

    问题描述: 一直卡死在某个断点处,取消断点也是卡死在这边的进程处。 解决方式: 将JDK的使用内存进行了修改 ① 打开eclipse,window-preference-Java-Installed JREs,选中使用的jdk然后点击右侧的edit,在Default VM Arguments中输入以下值 ② -Xms512m -Xmx512m -XX:MaxNewSize=512m -XX:MaxPermSize=512m ③

    2024年02月09日
    浏览(37)
  • 【STM32&RT-Thread零基础入门】 7. 线程创建应用(多线程运行机制)

    硬件:STM32F103ZET6、ST-LINK、usb转串口工具、4个LED灯、1个蜂鸣器、4个1k电阻、2个按键、面包板、杜邦线 本章进一步研究多线程的运行机制。要求实现功能如下:创建2个线程,线程名称分别为LED和BEEP。两个线程的任务是连续5次打印本线程的名字后退出线程(注意:线程不执行

    2024年02月03日
    浏览(27)
  • Java多线程&并发篇----第二十一篇

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 ArrayB

    2024年01月18日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包