java 线程 (一) 线程的简单使用

java 线程 (一) 线程的简单使用大家好,从今天开始,我和大家一起来探讨java中线程的使用。线程是java知识体系中非常重要的一部分,我将写一系列的文章来详细的介绍java线程中需要掌握的知识。如果你是java线程的初学者,本系列文章你一定不要错过哦。本篇文章是java线程系列文章的第一篇文章,主要介绍进程与线程的概念和java中如何使用线程。1进程与线程1.1进程的概念首先我们先来介绍一下什么是进程。进程可以理解为一个个正在执行的应用程序,比如我们使用网易云音乐软件播放音乐,同时我们在使用WP

大家好,又见面了,我是你们的朋友全栈君。

大家好,从今天开始,我和大家一起来探讨 java 中线程的使用。线程是 java 知识体系中非常重要的一部分,我将写一系列的文章来详细的介绍 java 线程中需要掌握的知识。如果你是 java 线程的初学者,本系列文章你一定不要错过哦。

本篇文章是 java 线程系列文章的第一篇文章,主要介绍进程与线程的概念和 java 中如何使用线程。

1 进程与线程

1.1 进程的概念

首先我们先来介绍一下什么是进程

进程可以理解为一个个正在执行的应用程序比如我们使用网易云音乐软件播放音乐,同时我们在使用 WPS 编辑我们的文档,并且还打开了 Chrome 浏览器查询资料等等。类似于下图:
在这里插入图片描述
这三个正在执行的应用程序就是我们的进程。这些进程之间是互不干扰的,即播放音乐的网易云音乐不会干扰到我们正在编辑文档的 WPS。

进程的定义:进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。简言之,就是一些处于运行状态的程序

1.1 线程的概念

首先还是先来体验一下什么是线程,比如我们在使用网易云播放音乐的时候,在听歌曲的同时我们会去浏览该歌曲相关的评论或者是自己发表一个评论等。
一般情况下,这时就有两个线程:一个是播放音乐的线程另一个是我们正在输入评论信息。这两个线程是互不干扰的,我们在发表评论时,音乐照样可以播放,并且这两个线程都是属于网易云音乐这个进程的。

线程的定义是操作系统能够进行运算调度的最小单位它被包含在进程之中是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,多条线程并行执行不同的任务

进程与线程的区别:进程只是一个动态的概念,代表的是程序代码在一个数据集上的一次运行过程。进程的主要作用就是获取到操作系统给它分配的硬件资源:包括用于存放程序正文、数据的磁盘和内存地址空间,以及在运行时所需要的I/O设备,已打开的文件,信号量等具体完成任务的是进程管理的线程

一个进程至少包含一个线程。

类似下图:
在这里插入图片描述

2 java 中如何创建线程

在了解了线程和进程的基本概念后,我们来学习一下 java 中的线程如何使用。

我们只需要关注如何创建和启动一个线程,关于进程的创建等是不需要我们考虑的,当我们启动一个 java 程序时,操作系统会创建进程。比如我们执行 main() 方法时,会启动 jvm 虚拟机,然后在虚拟机中执行我们的 main() 方法,虚拟机会分配一个线程来执行我们的 main() 方法,这个线程一般被称作主线程。

在 java 中有两种方式创建一个线程,我们下面分别介绍。

2.1 继承 Thread 类的方式

定义一个线程类,该线程的任务是每隔半秒输出一个数字。

PrintNumberThread.java:

public class PrintNumberThread extends Thread { 
   
    private int number;
    //线程要执行的任务写在 run() 方法中
    @Override
    public void run(){ 
   
        while(number<100){ 
   
            System.out.println(Thread.currentThread().getName()+":输出数字 "+number++);
            try { 
   
                Thread.sleep(500);
            } catch (InterruptedException e) { 
   
                e.printStackTrace();
            }
        }
    }
}

ThreadDemo.java:

public class ThreadDemo { 
   
	//创建一个线程实例
    PrintNumberThread printNumberThread = new PrintNumberThread();
    // 给线程设置名字
    printNumberThread.setName("printNumberThread");
    //启动线程
    printNumberThread.start();
}

在该类中创建一个 PrintNumberThread 类的实例,然后调用 start() 方法启动该线程实例,如果我们不调用 printNumberThread 对象的 start() 方法,该线程是不会执行的,它就和我们的普通的 java 对象一样

运行结果:
在这里插入图片描述
控制台会一直打印,直到 number 加到 100 才停止。
此段程序运行时至少启动了两个线程,一个是主线程,一个是我们自定义的 PrintNumberThread 线程。

2.2 实现 Runnable 接口

由于 java 中是单继承,通过继承 Thread 类的方式有时候就不难么方便呢,于是 java 还为我们提供了另外一种方式:实现 Runnable 接口

PrintNumberRunnable.java:

public class PrintNumberRunnable implements Runnable { 
   
    private int number;
    public void run() { 
   
        while(number<100){ 
   
            System.out.println(Thread.currentThread().getName()+":输出数字 "+number++);
            try { 
   
                Thread.sleep(500);
            } catch (InterruptedException e) { 
   
                e.printStackTrace();
            }
        }
    }
}

Runnable 接口只有一个 run() 方法需要我们实现,与 Thread 类的 run() 方法一样,我们只需要将想要完成的任务写在 run() 方法中即可。

ThreadDemo.java:测试类

public class ThreadDemo { 
   
    public static void main(String[] args) { 
   
        //创建一个线程实例
        PrintNumberRunnable printNumberRunnable = new PrintNumberRunnable();
        Thread thread = new Thread(printNumberRunnable);
        thread.setName("printNumberRunnable");
        thread.start();
    }
}

在创建线程实例时与前面不一样,需要先创建一个 PrintNumberRunnable 的实例 printNumberRunnable,再创建一个 Thread 类的实例 thread,并需要 PrintNumberRunnable 实例作为构造函数的参数,然后再调用 thread 的 start() 方法启动线程。

运行结果与前面的一样。

3 运行多个线程

经过前面的例子,大家可能没有看出使用线程和不使用线程的差别,下面我们再举一个例子来体验一下。

编写两个线程,一个线程每隔 1 秒输出一个数字,一个线程每隔 0.5 秒输出一个字母。

我们的目的是要感受到多个任务是可以同时执行的。比如我们用网易云听音乐时,播放音乐的同时,我们还可以写评论。

LetterThread.java: 每隔一秒输出一个字母

public class LetterThread extends Thread { 
   

    private char[] letters = { 
   'A','B','C','D','E'};
    //每隔 1 秒输出一个字母
    @Override
    public void run() { 
   
        String name = Thread.currentThread().getName();
        Long start = System.currentTimeMillis();
        System.out.println(name + " 输出字母开始时间:"+start);
        for (int i = 0;i<5;i++){ 
   

            System.out.println(name+" 输出字母:"+letters[i]);
            try { 
   
                Thread.sleep(1000);
            } catch (InterruptedException e) { 
   
                e.printStackTrace();
            }
        }
        Long end = System.currentTimeMillis();
        System.out.println(name + " 输出字母结束时间:"+end);
    }
}

NumberThread.java: 每隔半秒输出一个数字

public class NumberThread extends Thread { 
   

    //每隔 0.5 秒输出一个数字
    @Override
    public void run() { 
   
        String name = Thread.currentThread().getName();
        Long start = System.currentTimeMillis();
        System.out.println(name + " 输出数字开始时间:"+start);
        for (int i = 0;i<10;i++){ 
   
            System.out.println(Thread.currentThread().getName()+" 输出数字:"+i);
            try { 
   
                Thread.sleep(500);
            } catch (InterruptedException e) { 
   
                e.printStackTrace();
            }
        }
        Long end = System.currentTimeMillis();
        System.out.println(name + " 输出数字结束时间:"+end);
    }
}

Test.java: 测试类

public class Test { 
   
    public static void main(String[] args) { 
   
        NumberThread numberThread = new NumberThread();
        numberThread.setName("numberThread");
        //启动打印数字的线程
        numberThread.start();

        //启动打印字母的线程
        LetterThread letterThread = new LetterThread();
        letterThread.setName("letterThread");
        letterThread.start();
    }
}

运行结果:
在这里插入图片描述

从结果我们可以看出,两个线程几乎是在同一时刻启动,在同一时刻停止,差不多都运行了 5 秒。所以我们可以说他们是同时在执行的,即都在这 5 秒里完成了自己的任务。

大家还需要注意一点:虽然在代码中 numberThread.start(); 写在了 letterThread.start(); 的前面,但是不代表 numberThread 就一定先于 letterThread 启动;start() 方法只是告诉 CPU 当前线程需要启动,但是什么时候启动,就由 CPU 来决定了,我们就不能再控制它了。我们唯一能做的就是重写 run() 方法来定义一个线程需要完成什么任务和调用 start() 方法来启动这个线程。

所以前面的代码运行的时候也可能会出现 letterThread 比 numberThread 先启动的情况:
在这里插入图片描述

4 start() 方法 和 run() 方法

在前面的所有示例代码中,我们都重写了 Thread 类或者 Runabble 接口的 run() 方法。并在 run() 方法中定义这个线程需要完成的任务。但是启动线程时不是直接去调用线程实例的 run() 方法,而是通过调用 start() 方法来启动线程。需要注意的是,调用了 start() 方法后,线程可能不会立即执行,它需要等待 cpu 来调度。cpu 在处理该线程的任务时,其实就是执行我们定义的 run() 方法。

如果我们直接在代码中调用线程实例的 run() 方法,是没有多线程的效果的,run() 方法直接就在 main 线程中执行了,与执行一个普通方法没有什么区别。

所以我们应该调用线程实例的 start() 方法来启动一个线程。而不是直接调用 run() 方法。

下面我们还是拿前面的交替打印字母和数字的例子做一个举例:
使用 start() 方法启动两个线程的代码与前面一致,就不在写了。我们将直接调用 run() 方法的代码做一个简单例子:

Test01.java:

public class Test01 { 
   
    public static void main(String[] args) { 
   
        NumberThread numberThread = new NumberThread();
        numberThread.setName("numberThread");
        //调用 run() 方法
        numberThread.run();

        LetterThread letterThread = new LetterThread();
        letterThread.setName("letterThread");
        //调用 run() 方法
        letterThread.run();
    }
}

在这里插入图片描述
从上图中我们可以看出以下两点:
(1)输出数字的线程的名字不是我们设置的 numberThread ,而是 main 线程;输出字母的线程的名字也不是我们设置的 letterThread ,而是 main 线程。所以印证了我们前面的说法:如果直接调用线程实例对象的 run() 方法不会启动一个新的线程,而是直接在 main(主)线程中执行 run() 方法。
(2)从上图中我们可以看出,这段代码大约执行了 10 秒,

5 总结

本文就到此结束了,主要介绍了如下几个知识点:

  • 线程与进程
  • java 中如何创建线程、启动一个线程
  • start() 方法和 run() 方法的区别

示例代码地址:https://github.com/coderllk/threadDemo/tree/main/threadDemoOne

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/156347.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • ORA-12154 问题解决办法

    ORA-12154 问题解决办法问题 ORA-12154: TNS:couldnotresolvetheconnectidentifierspecified,即无法解析指定的连接标识符。这说明缺少了一个环境变量,TNS_ADMIN。    解决方法:右击我的电脑->属性 ->高级 ->环境变量->系统变量->新建,    变量名为:TNS_A

    2022年7月24日
    30
  • postman下载安装和基础使用教程(官网)

    postman下载安装和基础使用教程(官网)postman下载、安装、基础使用教程,亲测有效,有疑问可留言

    2022年6月25日
    30
  • java中常见的运行时异常_java五种常见异常

    java中常见的运行时异常_java五种常见异常运行时异常:编译时不会报错,但程序运行起来如果有错误就会报异常。以下为常见的运行时异常:ArithmeticException算数运算异常,由于除数为0引起的异常;ClassCastException类型转换异常,当把一个对象归为某个类,但实际上此对象并不是由这个类创建的,也不是其子类创建的,则会引起异常;ArrayStoreException由于数组存储空间不够引起的异…

    2022年9月1日
    6
  • python爬虫scrapy框架_nodejs爬虫框架

    python爬虫scrapy框架_nodejs爬虫框架叮铃铃!叮铃铃!老师:“小明你的梦想是什么?”,沉思了一下小明:“额额 我想有车有房,自己当老板,媳妇貌美如花,还有一个当官的兄弟”老师:“北宋有一个人和你一样···”;哈喽!大家好!请叫我布莱恩·奥复托·杰森张;爬虫部分!一提到爬虫,好多人先想到python没错就是那个py交易的那个,这货所为是什么都能干上九天揽月下五洋捉鳖无处不出现它的身影鄙人对它也是不得不折

    2026年1月15日
    4
  • Ajax请求的五个步骤[通俗易懂]

    Ajax请求的五个步骤[通俗易懂]Ajax请求的五个步骤一、定义1、什么是AjaxAjax:即异步JavaScript和XML。Ajax是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,Ajax可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。而传统的网页(不使用Ajax)如果需要更新内容,必需重载整个网页面。2、同步与异步的区别同步提交:当用户发送请求时,当前页面不可以使用,服务器响应页面到客户端,响应完成,用户才可以使用页面。异步提交:当用户发送请

    2022年5月17日
    63
  • Java 流程控制

    Java 流程控制

    2021年10月6日
    45

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号