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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 进程管理的模拟程序_实验一熟悉C语言的运行环境

    进程管理的模拟程序_实验一熟悉C语言的运行环境利用C语言模拟实现操作系统中的进程管理

    2022年9月11日
    0
  • [Err] 1062 – Duplicate entry ‘0’ for key ‘PRIMARY’

    [Err] 1062 – Duplicate entry ‘0’ for key ‘PRIMARY’

    2021年10月17日
    54
  • 1.如何实现MT4帐号同步交易?

    1.如何实现MT4帐号同步交易?使用本跟单EA,可以实现在同一台计算机上运行两个(或更多个)MetaTrader4自动复制交易。您使用其中一个MT4帐号手动交易或者使用EA自动交易,那么其它一个(或更多个)MT4,会立即复制此帐号中的订单。您可以运行多个喊单EA和多个跟单EA,可以实现一个帐号跟多个帐号,或者多个帐号跟一个帐号,又或者多个帐号跟多个帐号。用来喊单的MT4帐号不需要帐号必须拥交易权限,因此,可以使用MT4“投资者”密码登录。投资者密码,又称呼“只读密码、观摩密码”。…

    2022年5月30日
    32
  • 谷歌搜索引擎使用语法大全收集软件_搜索引擎语法

    谷歌搜索引擎使用语法大全收集软件_搜索引擎语法目录google介绍关于谷歌语法的一些例子Google常用语法踩点:需要收集的十个方面Google的一些入口地址谷歌语法详解需要注意以下几点:详细描述看以下几条链接:最后总结一下:看图片更直观GoogleHack技术是现在最火暴的黑客技术之一。其原理很简单,就是利用搜索引擎强大的搜索能力,来查找一些存在漏洞的网站。要利用Google来查找网站的漏洞自然要学会Google这个搜索引擎的语法了。下…

    2022年9月10日
    1
  • modis数据命名规则

    modis数据命名规则1. MOD是Terra星数据—-上午星;MYD是Aqua星数据—-下午星。2. Terra卫星原始数据命名规则>>>15位  A20040707140331.down                  A定义为Terra卫星;YYYY-MM-DD-HH-MM-SS(年-月-日-时-分-秒)

    2022年5月30日
    133
  • 地形教程 – 从图像到高度图

    地形教程 – 从图像到高度图HeightMapsFromImages从图像到高度图Nowthatweknowhowtoloadanimageletsseehowtocreateanddrawaheightmap.Thissectionpresentsasetoffunctionsfromtheterrainlibrarythatdealwithheigh

    2022年5月9日
    86

发表回复

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

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