创建、运行线程,设置线程属性

2023-10-31

Java 9并发编程指南 目录

创建、运行线程,设置线程属性

a中有两种方式创建一个线程。

  • 继承Thread 类,重写run()方法。
  • 创建一个类,实现Runnable接口和run()方法,然后通过Runnable对象作为参数来创建Thread类的一个对象。优先考虑这种方式,灵活性很高。

在本节中,使用第二种方法创建线程。然后学习如何改变线程属性。线程类保存了一些信息属性来帮助我们识别一个线程,知道它的状态,或者控制其优先级。这些属性包括:

  • ID:每一个线程的唯一标识符。
  • Name:线程的名称。
  • Priority:线程对象的优先级。在Java 9中,线程的优先级在1到10之间,1的优先级最低、10的优先级最高。我们不需要改变线程的优先级,这只是作为底层操作系统的一个线索,不保证任何操作。
  • Status:线程的状态。在Java中,线程的状态定义在Thread.State枚举类型中,分别是NEW,RUNNABLE,BLOCKED,WAITING,TIME_WAITING,TERMINATED。分别介绍如下:
    • NEW:已创建线程,但并未开始运行;
      • RUNNABLE:线程已经在JVM上执行;
      • BLOCKED:线程阻塞,并且等待监控器分配;
      • WAITING:线程等待另一个线程运行完再执行;
      • TIMED_WAITING:线程在指定等待时间内等待另一个线程运行完再执行;
      • TERMINATED:线程执行完成。

在本节中,通过范例展现创建和运行10个计算20000以内质数个数的线程。

准备工作

本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。

实现过程

通过如下步骤完成范例:

  1. 创建名为Calculator的类,实现Runnable接口:

    public class Calculator implements  Runnable {
    
  2. 实现run()方法,这个方法执行创建的线程指令,计算20000以内的质数个数:

        @Override
        public void run() {
            long current = 1L;
            long max = 20000L;
            long numPrimes = 0L;
    
            System.out.printf("Thread '%s' : START\n" , Thread.currentThread().getName());
    
            while (current <= max){
                if (isPrime(current)) {
                    numPrimes++;
                }
                current ++;
            }
    
            System.out.printf("Thread '%s' : END. Number of Primes : %d\n" ,            Thread.currentThread().getName(), numPrimes);
        }
    
  3. 然后,实现辅助方法isPrime()。这个方法确定一个数字是否是质数:

        private boolean isPrime(long number) {
            if (number <= 2) {
                return true;
            }
            for (long i = 2; i < number; i++){
                if ((number % i) == 0){
                    return false;
                }
            }
            return true;
        }
    
  4. 现在实现主类。创建一个包含main()方法的Main类:

    public class Main {
        public static void main(String[] args){
    
  5. 首先,输出线程的最大、最小和默认优先级值:

            System.out.printf("Minimun Priority : %s\n" , Thread.MIN_PRIORITY);
            System.out.printf("Normal Priority : %s\n" , Thread.NORM_PRIORITY);
            System.out.printf("Maximum Priority : %s\n" , Thread.MAX_PRIORITY);
    
  6. 然后创建10个线程对象执行10个Calculator任务。同时,创建两个队列保存线程对象和它们的当前状态。随后会用这些信息检查这些线程终止。用最高优先级执行五个线程(偶数列),最低优先级执行另外五个线程:

            Thread threads[];
            Thread.State status[];
            threads = new Thread[10];
            status = new Thread.State[10];
            for(int i = 0 ; i < 10 ; i++){
                threads[i] = new Thread(new Calculator());
                if((i % 2) == 0) {
                    threads[i].setPriority(Thread.MAX_PRIORITY);
                }else{
                    threads[i].setPriority(Thread.MIN_PRIORITY);
                }
                threads[i].setName("My Thread " + i);
            }
    
  7. 将输出信息写入到文本文件中,所以创建try-with-resources声明来管理文件。在这段代码块中,在开始线程前将线程的状态记录到文件中,然后,启动线程:

            try(FileWriter file = new FileWriter(System.getProperty("user.dir")+"\\log.txt");
                        PrintWriter pw = new PrintWriter(file)) {
    
                for(int i =0 ; i < 10 ; i++){
                    pw.println("Main : Status of Thread " + i + " : " + threads[i].getState());
                    status[i] = threads[i].getState();
                }
    
                for(int i = 0 ; i < 10 ; i++) {
                    threads[i].start();
                }
    
  8. 这之后,等待线程结束。在本章“等待线程结束”的课程里,可以使用join()方法实现这个功能。由于我们需要在线程状态改变时记录下线程信息,所以不适用这个方法。使用如下代码块:

            boolean finish = false;
            while (!finish){
                for (int i = 0 ; i < 10 ; i++){
                    if(threads[i].getState() != status[i]) {
                        writeThreadInfo(pw, threads[i], status[i]);
                        status[i] = threads[i].getState();
                    }
                }
    
                finish = true;
                for(int i = 0 ; i < 10 ; i++){
                    finish = finish && (threads[i].getState() == Thread.State.TERMINATED);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

  9. 在上述代码块中,调用writeThreadInfo()方法将线程状态信息记录到文件中。如下是这个方法实现代码:

    private static void writeThreadInfo(PrintWriter pw, Thread thread, Thread.State state){
        pw.printf("Main : Id %d - %s\n", thread.getId(), thread.getName());
        pw.printf("Main : Priority : %d\n" , thread.getPriority());
        pw.printf("Main : Old State : %s\n", state);
        pw.printf("Main : New State : %s\n" , thread.getState());
        pw.printf("Main : **************************************************\n");
    }
    
  10. 运行程序,查看不同的线程如何并行工作的。

工作原理

下面的截图展现程序在控制台输出的部分信息。可以看到所有创建的线程在同时执行它们各自的工作:
pics/01_01.jpg
在截图中,可以看到如何创建线程,以及偶数列线程因为有最高优先级而先执行,其他线程由于有最低优先级而后续执行。下面的截图显示输出的log.txt记录的部分线程状态信息。
pics/01_02.jpg
所有的Java程序都至少包含一个执行线程。当运行程序时,JVM运行程序中调用main()方法执行线程。

当调用Thread对象的start()方法时,将创建另一个执行线程。程序中包含与调用start()方法同样多的执行线程。

Thread类属性存储线程的所有信息。操作系统调度器始终通过线程优先级确定使用处理器的线程,同时根据当前情况实现每个线程的状态。

如果尚未给线程指定名称,JVM按照格式Thread-XX自动命名,其中XX是数字。我们无法修改线程的ID和状态,Thread类未实现setId()和setStatus()方法,因为这些方法在代码中实现修改操作。

当所有线程运行结束时(更具体的,当所有非守护线程运行结束时),Java程序才会终止。如果初始线程(执行main()方法的线程)终止,其他线程将继续执行之道结束。如果其中一个线程使用System.exit()指令来终止程序执行,所有的线程都将终止其各自执行。

在Thread类中创建对象,以及调用实现Runnable接口的类的run()方法都不会创建一个新的执行线程。只有调用start()方法时才会创建一个新的执行线程。

扩展学习

如本节介绍中提及的,还有一种创建执行线程的方法。继承Thread 类,重写run()方法。然后创建对象并调用start()方法获得一个新的执行线程。

通过Thread类的currentThread()方法使用正在运行当前对象的线程。

需要考虑的是,如果用setPriority()方法尝试设置线程优先级不在1-10之间,会抛出IllegalArgumentException异常。

更多关注

  • 本章中“工厂模式创建线程”小节。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

创建、运行线程,设置线程属性 的相关文章

随机推荐

  • ARouter 基础使用详解

    文章目录 参考资料 配置ARouter 初始化与销毁 初始化 销毁 路由注解与跳转 Activity之间的跳转 路径定义 代码示例 Fragment之间的切换 Fragment注入路由 Fragment的获取与切换 带参数的跳转 示例代码
  • Springboot yml 复杂对象的二维数组

    有两种形式可以表达这种需求 一 通过建立一个二维维数组来表述 1 Grid类 Data AllArgsConstructor NoArgsConstructor public class Grid Long x 栅格中心位置 x 值 Lon
  • 挖矿病毒排查并清除

    近期 公司内网linux环境出现了挖矿病毒 该病毒占满cpu进行挖矿 导致系统缓慢 现摸索了以下步骤进行清除 1 检测服务器是否有挖矿病毒 使用top命令查看进程及占用cpu百分比 如果该进程名称为随机字符串 且cpu占的非常的高 则很可能
  • OSPF篇——SPF算法——002

    目录 最短路径优先算法 SPF 阶段1 构建SPF树 阶段2 计算最优路由 最短路径优先算法 SPF 在一类LSA和二类LSA中 包括了拓扑信息和路由信息 OSPF将依据SPF算法和各类LSA LSA内既包含拓扑信息又包含路由信息 但是分开
  • 关于VS Code 断点失效(断点变空心)

    问题 VS code 断点再有的文件中能正常使用 有的文件中打断点会变成灰色空心 这部分代码没问题能正常运行 但是断点失效无法重断点处停下来 解决方案 1 百度查看相似案例解决方案 2 这是我的问题解决方法 查看代码是否有问题 VScode
  • linux系统之字符设备驱动——DS18B20温度传感器

    linux系统之字符设备驱动 DS18B20温度传感器 1 原理图 2 驱动程序 驱动程序 ds18b20 c Author your name Date 2021 02 06 19 41 29 LastEditTime 2021 02 2
  • css根据内容自动调整td高度,css – 如何使div高度100%内部td的100%

    这个问题似乎在stackoverflow上至少有10次 但是其中一个实际上没有答案 这一点略有不同 因为问题出现在Firefox中 我的桌子高度为100 高度为100 我把td的边界设置成可以看到的东西 我看到td是预期的100 我把一个d
  • JS前端点击记住密码之后再次登录时显示账号密码

    一 前端html代码 div div
  • Nacos下载与安装详解

    目录 一 安装与下载 二 数据持久化 三 docker当中安装nacos 一 安装与下载 下载地址 https github com alibaba nacos releases 我这里下载的windows版本的 不需要安装 下载好直接解压
  • requests.exceptions.InvalidHeader: Value for header {XX: (‘XX‘,)} must be of type str or bytes, not

    requests exceptions InvalidHeader Value for header xxx xxx must be of type str or bytes not
  • Http协议及各版本对比

    前言 本文主要简单介绍http协议发展的历史版本以及https的安全机制 对于更多深入协议内层及网络通信相关的知识 在此暂不做总结 何谓Http协议 超文本传输协议 Hypertext Transfer Protocol HTTP 是一个简
  • echart单系列柱状图增加配置筛选legend

    先看整体效果图 这边简单说下实现思路 单列柱子在官网demo是没有legend的 但是多列柱子是有的 因此 我们可以让单列柱子变成多列柱子的集合 然后集合里面只有一列是有data的 相当于是这样的集合 0 1 0 然后使用重叠配置 把那些空
  • 安卓9.0适配方案和踩坑

    年初的时候就已经适配了安卓9 0 但由于业务需求一直没有使用上 前段时间发布了 结果有用户反馈在安卓9 0的手机上更新下载App发生了闪退 这个时候发现9 0对权限 加密和Apache HTTP client发生了相关变化 一 首先我遇到的
  • matlab解决线性规划

    线性规划 线性规划 Linear programming 简称LP 是运筹学中研究较早 发展较快 应用广泛 方法较成熟的一个重要分支 它是辅助人们进行科学管理的一种数学方法 研究线性约束条件下线性目标函数的极值问题的数学理论和方法 英文缩写
  • 数据库间歇性失败 OERR: ORA-12519

    ORA 12519 TNS 没有找到适用的服务处理 OERR ORA 12519 TNS no appropriate service handler found 客户端连接间歇性失败 报错ORA 12519 Oracle客户端与服务器之间
  • select、poll、epoll之间的区别(搜狗面试)(转载)

    1 select gt 时间复杂度O n 它仅仅知道了 有I O事件发生了 却并不知道是哪那几个流 可能有一个 多个 甚至全部 我们只能无差别轮询所有流 找出能读出数据 或者写入数据的流 对他们进行操作 所以select具有O n 的无差别
  • 云服务器文件打包,如何把云服务器的文件打包出来

    如何把云服务器的文件打包出来 内容精选 换一换 文档数据库服务支持开启公网访问功能 通过弹性IP进行访问 您也可通过弹性云服务器的内网访问文档数据库 要将已有的MongoDB数据库迁移到文档数据库 需要先使用mongoexport工具对它做
  • boa的cgi使用总结

    相关配置 配置ScriptAlias 虚拟路径 真实路径 ScriptAlias cgi bin etc boa www cgi bin 指明CGI脚本的虚拟路径对应的实际路径 一般所有的CGI脚本都要放在实际路径里 用户访问执行时输入站点
  • 如何让 uni-app 页面中的背景图片高度和宽度自适应

    如何让 uni app 页面中的背景图片高度和宽度自适应 在今天做项目的时候突然遇到一个问题 我给页面加一个背景图片 但是当页面高度超过100 时 图片会覆盖不到 如图所示 写的代码是这样的 错误的代码 App vue 页面
  • 创建、运行线程,设置线程属性

    Java 9并发编程指南 目录 创建 运行线程 设置线程属性 准备工作 实现过程 工作原理 扩展学习 更多关注 a中有两种方式创建一个线程 继承Thread 类 重写run 方法 创建一个类 实现Runnable接口和run 方法 然后通过