目录
多线程的创建 
方式一:继承Thread类 
方式二: 实现Runnable接口
方式三:实现Callable接口 
总结 
常用方法 
Thread获取和设置线程名称 
Thread类的线程休眠方法 
首页 常见问题 一文搞懂java中的线程如何理解

一文搞懂java中的线程如何理解

Jul 18, 2022 pm 01:45 PM
java

本篇文章给大家带来了关于java的相关知识,其中主要整理了线程的相关问题,线程(thread)是一个程序内部的一条执行路径,我们所熟悉的main方法其实就是一条单独执行路径,若程序中只有一条执行路径那么这个程序就是单线程程序,下面一起来看一下,希望对大家有帮助。

一文搞懂java中的线程如何理解

推荐学习:《java视频教程

线程(thread)是一个程序内部的一条执行路径,我们所熟悉的main方法其实就是一条单独执行路径,若程序中只有一条执行路径那么这个程序就是单线程程序;既然有单线程,那么也相对的会有多线程,字面意思可以理解为“相对单线程从软硬件上多条执行流程的技术”,多线程的好处是提高CPU的利用率。 在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,大大提高程序的效率。

多线程的创建 

方式一:继承Thread类 

方式一创建过程:

  • 定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法;

  • 创建MyThread类的对象;

  • 调用线程对象的start()方法启动线程(启动后仍然执行run()方法); 

    public class ThreadDemo01 {
        public static void main(String[] args) {
            MyThread myThread1 = new MyThread();
            myThread1.start();
            for (int i = 0; i < 3; i++) {
                System.out.println("主线程正在执行~~");
            }
        }
    }
    class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println("子线程正在执行~~");
            }
    
        }
    }
    //输出结果(不唯一):
    //主线程正在执行~~
    //主线程正在执行~~
    //主线程正在执行~~
    //子线程正在执行~~
    //子线程正在执行~~
    //子线程正在执行~~
    登录后复制
  • 在以上代码中共有两个线程在执行,分别是main方法的主线程和线程对象mythread调用start()启动的子线程。不过输出结果为什么会不唯一的呢?原因是因为两个线程在执行过程中会发生CPU的抢占,谁先抢到谁将优先执行。

那么我们为什么不直接使用线程对象调用run()方法呢?若直接调用run()则只是普通的调用方法,即单线程,而start()方法则是用来启动的子线程的,由此才能出现多线程。

方式一优缺点:

  • 优点:编码简单;
  • 缺点:线程类已经继承Thread,无法继承其他类,不利于扩展;

方式二: 实现Runnable接口

方式二创建过程:

1、定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法;

2、创建MyRunnable对象;

3、把MyRunnable任务对象交给Thread处理;

4、调用线程对象的start()方法启动线程;

Thread构造器方法
public Thread (String name)可以为当前线程指定名称
public Thread (Runnable target)封装Runnable对象成为线程对象
public Thread (Runnable target ,String name)封装Runnable对象成为线程对象,并指定线程名称
public class ThreadDemo02 {
    public static void main(String[] args) {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target);
        thread.start();
        for (int i = 0; i < 3; i++) {
            System.out.println("主线程正在执行~~");
        }
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("子线程正在执行~~");
        }

    }
}
//输出结果(不唯一):
//主线程正在执行~~
//子线程正在执行~~
//子线程正在执行~~
//子线程正在执行~~
//主线程正在执行~~
//主线程正在执行~~
登录后复制

该代码与方式一的不同之处在于需要将MyRunnable任务对象封装在Thread中,其他的地方是基本上是没有变化的。

方式二优缺点:

优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;

缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。

接下来我们同样使用实现Runnable接口(匿名内部类形式)来实现多线程的创建:

1、创建Runnable匿名内部类对象;

2、交给Thread处理;

3、调用线程对象的start()启动线程;

//正常版:
public class ThreadDemo01 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    System.out.println("子线程正在执行~~");
                }
            }
        });
        thread.start();
        for (int i = 0; i < 3; i++) {
            System.out.println("主线程正在执行~~");
        }
    }
}

//lambda简化版:
new Thread(()-> {
                for (int i = 0; i < 3; i++) {
                    System.out.println("子线程正在执行~~");
                }
        }).start();
登录后复制

该种方法从本质上其实并没有太大的区别只不过是一个需要创建线程对象,而另一个则是通过匿名内部类实现的多线程。并且该块代码也可以通过lambda表达式进行精简,不知道大家是否还对这个知识点有印象呢?若忘记了可以看一下这篇文章:Java中的lambda表达式如何理解——精简

方式三:实现Callable接口

在学习过前面两种创建多线程的方式以后,我们会发现存在一个问题:1、重写的run()方法不能直接返回结果;2、不适合需要返回线程执行结果的业务场景。因此,我们需要第三种方式来解决这些问题。

方式三创建过程:

1、定义类实现Callable接口,重写call()方法,封装要做的事情;

2、用FutureTask把Callable对象封装成线程任务对象;

3、把线程任务对象交给Thread处理;

4、调用Thread的start()方法启动线程,执行任务;

5、线程执行完毕后,通过FutureTask的get()方法获取任务执行的结果。

public class ThreadDemo03 {
    public static void main(String[] args) throws Exception {
        MyCallable myCallable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(myCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        int sum= 0;
        for (int i = 0; i < 3; i++) {
            sum+=i;
        }
        System.out.println(sum);
        String s =futureTask.get();
        System.out.println(s);
    }
}
class MyCallable implements Callable<String > {
    @Override
    public String call(){
        int sum=0;
        for (int i = 0; i < 3; i++) {
            sum+=i;
        }
        return "子线程计算结果:"+sum;
    }
}
//输出结果:
//3
//子线程计算结果:3
登录后复制

方式三优缺点:

优点:

线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;

可以在线程执行完毕后去获取 线程执行的结果;

缺点:

编码复杂一点;

总结 

方法名称说明
public FutureTask<>(Callable call)把Callable对象封装成FutureTask对象
public V get() throws Exception 获取线程执行call方法返回的结果
方式 优点 缺点
继承Thread类 编程比较简单,可以直接使用Thread类中的方法 扩展性较差,不能再继承其他的类,不能返回线程执行的结果
实现Runnable接口 扩展性强,实现该接口的同时还可以继承其他的类 编程相对复杂,不能返回线程执行的结果
实现Callable接口 扩展性强,实现该接口的同时还可以继承其他的类,可以得到线程的执行结果 编程相对复杂

常用方法 

Thread获取和设置线程名称 

方法名称 说明
String getName() 获取当前线程的名称,默认线程名称是Thread-索引
void setName(String name)

将此线程更改为指定的名称,通过构造器也可以设置线程名称

 简单地通过一段代码让大家能够清晰地了解这个代码该如何使用:

public class ThreadDemo04 {
    public static void main(String[] args) throws Exception {
        thread thread1 = new thread();
        thread1.setName("1号子线程");
        thread1.start();
        thread thread2 = new thread();
        thread2.setName("2号子线程");
        thread2.start();
    }
}
class thread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(this.getName()+"正在执行任务"+i);
        }
    }
}
//输出结果:
//2号子线程正在执行任务0
//1号子线程正在执行任务0
//2号子线程正在执行任务1
//1号子线程正在执行任务1
//2号子线程正在执行任务2
//1号子线程正在执行任务2
登录后复制

Thread类的线程休眠方法 

方法名称 说明
public static void sleep(long time)  让当前线程休眠指定的时间后再继续执行,单位为毫秒
public class ThreadDemo05 {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
            if (i==3){
                Thread.sleep(5000);
            }
        }
    }
}
//输出结果:
//1
//2
//3
//在输出过3以后,等待5秒之后再进行输出
//4
登录后复制

推荐学习:《java视频教程

以上是一文搞懂java中的线程如何理解的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Java 中的完美数 Java 中的完美数 Aug 30, 2024 pm 04:28 PM

Java 完美数指南。这里我们讨论定义,如何在 Java 中检查完美数?,示例和代码实现。

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。这里我们通过示例讨论简介、如何使用weka java、平台类型和优点。

Java 中的史密斯数 Java 中的史密斯数 Aug 30, 2024 pm 04:28 PM

Java 史密斯数指南。这里我们讨论定义,如何在Java中检查史密斯号?带有代码实现的示例。

Java Spring 面试题 Java Spring 面试题 Aug 30, 2024 pm 04:29 PM

在本文中,我们保留了最常被问到的 Java Spring 面试问题及其详细答案。这样你就可以顺利通过面试。

突破或从Java 8流返回? 突破或从Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一种强大且表达力丰富的处理数据集合的方式。然而,使用Stream时,一个常见问题是:如何从forEach操作中中断或返回? 传统循环允许提前中断或返回,但Stream的forEach方法并不直接支持这种方式。本文将解释原因,并探讨在Stream处理系统中实现提前终止的替代方法。 延伸阅读: Java Stream API改进 理解Stream forEach forEach方法是一个终端操作,它对Stream中的每个元素执行一个操作。它的设计意图是处

Java 中的时间戳至今 Java 中的时间戳至今 Aug 30, 2024 pm 04:28 PM

Java 中的时间戳到日期指南。这里我们还结合示例讨论了介绍以及如何在java中将时间戳转换为日期。

Java程序查找胶囊的体积 Java程序查找胶囊的体积 Feb 07, 2025 am 11:37 AM

胶囊是一种三维几何图形,由一个圆柱体和两端各一个半球体组成。胶囊的体积可以通过将圆柱体的体积和两端半球体的体积相加来计算。本教程将讨论如何使用不同的方法在Java中计算给定胶囊的体积。 胶囊体积公式 胶囊体积的公式如下: 胶囊体积 = 圆柱体体积 两个半球体体积 其中, r: 半球体的半径。 h: 圆柱体的高度(不包括半球体)。 例子 1 输入 半径 = 5 单位 高度 = 10 单位 输出 体积 = 1570.8 立方单位 解释 使用公式计算体积: 体积 = π × r2 × h (4

创造未来:面向零基础的 Java 编程 创造未来:面向零基础的 Java 编程 Oct 13, 2024 pm 01:32 PM

Java是热门编程语言,适合初学者和经验丰富的开发者学习。本教程从基础概念出发,逐步深入讲解高级主题。安装Java开发工具包后,可通过创建简单的“Hello,World!”程序实践编程。理解代码后,使用命令提示符编译并运行程序,控制台上将输出“Hello,World!”。学习Java开启了编程之旅,随着掌握程度加深,可创建更复杂的应用程序。