多线程(JavaEE初阶系列2)

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

目录

前言:

1.什么是线程

2.为什么要有线程

3.进程与线程的区别与联系

4.Java的线程和操作系统线程的关系

5.多线程编程示例

6.创建线程

6.1继承Thread类

 6.2实现Runnable接口

6.3继承Thread,使用匿名内部类

6.4实现Runnable接口,使用匿名内部类

6.5lambda表达式创建Runnable子类对象

7.Thread类及常见的方法

7.1Thread中常见的构造方法

7.2Thread的几个常见属性

结束语:


前言:

这节中小编就来和大家聊一聊多线程是什么以及需要我们掌握多线程程序的编写、多线程的状态、什么是线程不安全及解决思路以及掌握synchronized、volatile关键字。在上节博客中给大家讲到了进程和进程管理,我们讲解了为什么要使用调度,CPU的按照并发的方式来执行进程的,在PCB中也提供了一些属性,里面有进程的优先级、进程的状态、进程的上下文、进程的记账信息......也给大家讲解了引入进程的目的就是为了能够实现多个任务并发执行的效果。接下来小编就给大家讲解一下什么是线程,线程与进程之间又有什么联系。

1.什么是线程

一个线程就是一个“执行流”,每个线程之间都可以按照顺序执行自己的代码,多个线程之间“同时”执行着多份代码。

2.为什么要有线程

首先“并发编程”称为“刚需”。

  • 单核CPU的发展遇到了瓶颈,要想提高算力,就需要多核CPU,而并发编程能够更充分利用多核CPU资源。
  • 有些任务场景需要“等待IO”,为了让等待IO的时间能够去做一些其他的工作,也需要用到并发编程。 

其次,虽然多进程也能实现并发编程,但是是线程比进程更轻量。

  • 创建线程比创建进程更快。
  • 销毁线程比销毁进程更快。
  • 调度线程比调度进程更快。

最后,线程虽然比进程轻量,但是人们还不满足,于是又有了“线程池”和“协程”。

  • 有关于线程池的话题我们后面在介绍,关于协程的话题这里我们不做过多的讨论。

3.进程与线程的区别与联系

进程有一个重大的问题就是比较重量,如频繁的创建/销毁进程,成本会比较高。

所以我们又引出了线程的概念。那么线程与进程之间到底有什么联系和区别呢?我们接着往下看。

进程是包含线程的。每一个进程至少有一个线程存在,即主线程。一个进程里可以有一个线程也可以有多个线程,每个线程之间都是一个独立存在的执行流,多个线程之间也是并发执行的。这里注意多个线程可能是在多个CPU核心上同时运行,也可能是在一个CPU核心上,通过快速调度,并发运行。操作系统真正的调度是在调度线程而不是在调度进程。

注意:线程是操作系统调度运行的基本单位!!!进程是操作系统资源分配的基本单位!!!

一个进程中的多个线程之间,共用一份系统资源。

  • 内存空间。
  • 文件描述符表。

注意:只有在进程启动的时候,创建第一个线程的时候需要花成本去申请系统资源。一旦进程(第一个线程)创建完毕,此时后续再创建线程就不必在申请资源了,这样创建和销毁的效率就会大大提高。

 下面小编给大家举个例子方便大家更好的理解进程与线程。

多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

  所以通过上述的例子再给大家总结一下进程与线程之间的区别:

  • 进程包含线程。
  • 进程有自己独立的内存空间和文件描述符表,同一个进程中的多个线程之间共享一份地址空间和文件描述符表。
  • 进程是操作系统资源分配的基本单位,线程是操作系统调度执行的基本单位。
  • 进程之间具有独立性,一个进程挂了不会影响其他进程;同一个进程里的多个线程之间,一个线程挂了,可能会把整个进程带走,影响其他进程。

4.Java的线程和操作系统线程的关系

线程是操作系统中的概念,操作系统内核实现了线程这样的机制,并且对用户层提供了一些API供用户使用。

Java标准库中Thread类可以视为是对操作系统提供的API进行了进一步的抽象和封装。

5.多线程编程示例

上面说了这么多的概念,接下来还是带着大家一起在代码中切实的感受一下究竟什么是多线程。

代码展示:

package Thread;
class MyThread extends Thread{
    @Override
    public void run() {
        while (true) {
            System.out.println("hello t");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        //start会创建新的线程。
        t.start();
        //run不会创建新的线程。run是在main线程中执行的。
        //t.run();
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


结果展示:
多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

代码分析:

在上述代码中我们看到我们写了一个类MyThread并继承了Thread类,在java中标准库提供了一个类Thread,它能够表示一个线程。

在继承Thread的时候我们需要对父类中的run()方法进行重写。

  • 在主函数中我们先创建出来了一个MyThread的实例。

Thread t = new MyThread();

  •  紧接着我们就要启动线程。

t.strat();

在上述代码中我们涉及到了两个线程。

  • main方法所对应的线程(一个进程里面至少有一个线程)也可以称为主线程。
  • 通过t.strat创建一个新的线程。

结果分析:
我们执行代码之后看到的效果是“hello t”和“hello main”是交替打印的,但又不是严格意义上的交替打印。按照我之前的单线程的想法,如果我们执行main线程中的进入while循环之后由于是死循环,那么按照之前的想法应该是出不来的,应该是要一值打印“hello main”的,但是这里并不是我们想象中的那样,而是和另一个线程中的一起交替打印,那就说明这里是启动了两个线程,两个线程分别独立运行。多线程在CPU上的调度是不确定的是随机的。所以我们看到的就是不规律的交替打印。这也就是我们上述中提到的一个进程中的多个线程并发执行的过程。

同时我们可以借助jak里面的工具jconsole来分析java里的线程。

在下面的路径下找到安装路径,然后双击打开运行。

多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

 点击线程,在下面可以看到很多线程这里你会发现有很多线程,这里不只有咱们创建出来的两个。除了标记出来的两个线程之外其他都是JVM自己创建出来的。

多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言 多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

描述出了当前这两线程执行到哪里了。

6.创建线程

6.1继承Thread类

我们可以使用Thread,重写run方式来创建线程。

代码展示:

package Thread;
//使用继承Thread,重写run方法来创建线程
class MyThread extends Thread{
    @Override
    public void run() {
        while (true) {
            System.out.println("hello t");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        //start会创建新的线程。
        t.start();
        //run不会创建新的线程。run是在main线程中执行的。
        //t.run();
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


结果展示:

多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

 6.2实现Runnable接口

使用实现Runable,重写run。

代码展示:

package Thread;
//使用Runnable接口来实现线程创建
//1.实现Runnable接口
class MyRunnable implements Runnable{
    @Override
    public void run() {
        while (true) {
            System.out.println("hello t");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        //2.创建爱你Thread类实例,调用Thread的构造方法是将Runnable对象作为target参数。
        MyRunnable runnable = new MyRunnable();
        Thread t = new Thread(runnable);
        //3.调用start方法。
        t.start();
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

结果展示:

多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

对比上述两种方法:

  • 继承Thread类,直接使用this就表示当前线程对象的引用。
  • 实现Runnable接口,this表示的是MyRunnable的引用,需要使用Thread.currentThread()

6.3继承Thread,使用匿名内部类

代码展示:

package Thread;
//继承Thread,使用匿名内部类来创建线程
public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello t");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        //启动线程
        t.start();
        //主线程
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


结果展示:
多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

6.4实现Runnable接口,使用匿名内部类

代码展示:

package Thread;

public class ThreadDemo4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello t");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        //启动线程
        t.start();
        //主线程
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


结果展示:
多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

6.5lambda表达式创建Runnable子类对象

这个也是创建线程最推荐的写法,使用lambda表达式也是最直观的简单的写法!!!

前面也给大家讲解过有关于lambda表达式的用法,如果还有不会的同学请点击下面的链接先去看看lambda表达式的使用吧!!!(http://t.csdn.cn/zEPFB)

代码展示:

package Thread;
//使用lambda表达式来创建线程
public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //启动线程
        t.start();
        //主线程
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


结果展示:
多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

7.Thread类及常见的方法

Thread类是JVM用来管理线程的一个类,换句话说,每个线程都由一个唯一的Thread对象与之关联。Java代码中的Thread对象和操作系统中的线程是一一对应的。

用我们上述的例子来看,每个执行流,也需要有一个对象来描述,类似下图所示,而Thread类的对象就是用来描述一个线程执行流的,JVM会将这些Thread对象组织起来,用于线程调度,线程管理。

多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

7.1Thread中常见的构造方法

方法

说明

Thread() 创建线程对象
Thread(Runnable target) 使用Runnable对象创建线程对象
Thread(String name) 创建线程对象,并命名
Thread(Runnable target,String name) 使用Runnable对象创建线程对象,并命名
Thread(ThreadGroup group,Runnable target) 线程可以被用来分组管理,分好的组即为线程组,这个目前我们了解即可

代码演示案例:

package Thread;

public class ThreadDemo6 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
           while (true) {
               System.out.println("hello t");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        },"我的线程");
        t.start();
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


结果展示:

多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

 我们可以看到在上述运行的线程中就会出现自己命名的线程名字。

7.2Thread的几个常见属性

属性 获取
ID  getId()
名称

getName()

状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()

代码展示:

package Thread;

public class ThreadDemo7 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
//            System.out.println("hello t");
        },"我的线程");
        t.start();
        System.out.println("线程ID是:" + t.getId());
        System.out.println("线程名称是:" + t.getName());
        System.out.println("线程状态是:" + t.getState());
        System.out.println("线程优先级是:" + t.getPriority());
        System.out.println("线程是否是后台线程:" + t.isDaemon());
        System.out.println("线程是否存活:" + t.isAlive());
        System.out.println("线程是否被中断:" + t.isInterrupted());
    }
}

结果展示:
多线程(JavaEE初阶系列2),JavaEE初阶,java,jvm,开发语言

解释getDaemon:

  • true表示是后台线程。后台线程不会阻止java进程结束,哪怕后台线程还没有执行完,java进程该结束就会结束。
  • false表示是前台线程。前台线程会阻止java进程结束,必须得java进程中的所有前台线程都执行完java进程才会结束。

注意:创建的线程默认是前台的,也可以通过setDaemon修改成后台线程。

解释isAlive:
是描述系统内核里的那个线程是否还存活。线程的入口方法执行完毕,此时系统中的对应的线程就没有了,此时调用该线程isAlive就是false。

结束语:

这节中小编主要是给大家分享了线程了概念、线程与进程之间的区别和联系以及如何创建线程。希望这节对大家学习JavaEE有一定的帮助,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)文章来源地址https://www.toymoban.com/news/detail-608553.html

到了这里,关于多线程(JavaEE初阶系列2)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【JavaEE初阶】了解JVM

    JVM启动的时候,会申请到一整个很大的内存区域.JVM是一个应用程序,要从操作系统里申请内存.JVM就根据需要,把空间分为几个部分,每个部分各自有不同的功能.具体划分如下: Native Method Stacks(本地方法栈) :native表示是JVM内部的C++代码.就是给调用native方法(JVM内部的方法)准备的栈空

    2024年02月13日
    浏览(57)
  • 《JavaEE初阶》JVM基础知识

    本章主要介绍JVM中比较重要的三个内容: JVM内存区域划分 JVM类加载机制 JVM垃圾回收机制 当我们创建一个java进程时,启动时会向操作系统申请一块内存,JVM会将这块内存划分为几个区域: 堆 栈 程序计数器 方法区 堆: 堆上主要存放的是new的对象,是最重要的区域,也是内存划分最多

    2024年02月09日
    浏览(37)
  • JavaEE初阶:多线程 - 编程

    我们在之前认识了什么是多进程,今天我们来了解线程。 一个线程就是一个 \\\"执行流\\\". 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 \\\"同时\\\" 执行 着多份代码. 引入 进程 这个概念,主要是为了解决并发编程这样的问题。因为cpu进入了多核心的时代,要想进一步

    2024年02月12日
    浏览(35)
  • javaee初阶———多线程(三)

    T04BF 👋专栏: 算法|JAVA|MySQL|C语言 🫵 小比特 大梦想 此篇文章与大家分享多线程专题第三篇,关于 线程安全 方面的内容 如果有不足的或者错误的请您指出! 我们在前面说过,线程之间是抢占式执行的,这样产生的随机性,使得程序的执行顺序变得不一致,就会使得程序产生不同的结

    2024年04月16日
    浏览(38)
  • 【JavaEE初阶】 线程安全

    线程安全是多线程编程是的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且准确的执行,不会出现数据污染等意外情况。上述是百度百科给出的一个概念解释。换言之,线程安全就是某

    2024年02月08日
    浏览(51)
  • 【JavaEE初阶】线程的概念与创建

    本节目标 认识多线程 创建多线程 Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 一个线程就是一个 “执行流” . 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之

    2024年02月07日
    浏览(38)
  • 【JavaEE初阶】 线程池详解与实现

    线程池,是一种线程的使用模式,它为了降低线程使用中频繁的创建和销毁所带来的资源消耗与代价。 通过创建一定数量的线程,让他们时刻准备就绪等待新任务的到达,而任务执行结束之后再重新回来继续待命。 想象这么一个场景: 在学校附近新开了一家快递店,老板很

    2024年02月06日
    浏览(35)
  • 【JavaEE初阶】 线程安全的集合类

    原来的集合类, 大部分都不是线程安全的. Vector, Stack, HashTable, 是线程安全的(不建议用), 其他的集合类不是线程安全的. 为什么不建议使用呢? 因为我们在使用的时候,这些类就会自动的加锁,虽然编译器会自动优化为没有锁竞争的线程进行锁消除的优化,但是呢万一编译器没

    2024年02月08日
    浏览(33)
  • JavaEE初阶Day 6:多线程(4)

    前序 :针对Day 5结尾的count++ 多线程的执行,是 随机调度抢占式 的执行模式,某个线程执行指令过程中,当它执行到任何一个指令的时候,都有可能被其他线程把它的CPU抢占走 实际并发执行,由于 上述原因 以及 count++本质是CPU的三个指令 ,两个线程执行指令的相对顺序就可

    2024年04月14日
    浏览(28)
  • 【JavaEE初阶】多线程(四)阻塞队列 定时器 线程池

    概念 阻塞队列是一种特殊的队列. 也遵守 “ 先进先出 ” 的原则. 阻塞队列能是一种线程安全的数据结构, 并且具有以下特性: 当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素. 当队列空的时候, 继续出队列也会阻塞,直到有其他线程往队列中插入元素

    2023年04月26日
    浏览(53)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包