Java多线程编程之创建子线程

简介: 学习线程之前先了解一下有关的基础知识

一、基础知识


学习线程之前先了解一下有关的基础知识


(1)进程

一般可以在同一时间内执行多个程序的操作系统都有进程的概念。一个进程就是一个执行中的程序, 而每一个进程都有自己独立的一块内存空间、一组系统资源。在进程的概念中,每一个进程的内部数 据和状态都是完全独立的。


(2)线程

线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序控制的流程,但与进程不同的 是,同类的多个线程是共享一块内存空间和一组系统资源。所以系统在各个线程之间切换时,开销要 比进程小的多,正因如此,线程被称为轻量级进程。一个进程中可以包含多个线程。


(3)主线程

Java程序至少会有一个线程,这就是主线程,程序启动后是由JVM创建主线程,程序结束时由JVM停 止主线程。主线程它负责管理子线程,即子线程的启动、挂起、停止等等操作。图所示是进程、 主线程和子线程的关系,其中主线程负责管理子线程,即子线程的启动、挂起、停止等操作。

2.png

获取主线程示例代码如下:

public class HelloWorld {
    public static void main(String[] args) {
//        获取主线程
        Thread mainThread = Thread.currentThread();
        System.out.println("主线程名"+mainThread.getName());
    }
}

运行结果:

主线程名main

Thread.currentThread()获得当前线程,由于在main()方法中当前线程就是主线程,

Thread是Java线程类,位于java.lang包中

getName()方法获得线程的名字,主线程名是 main,由JVM分配。


二、创建子线程


Java中创建一个子线程涉及到:

java.lang.Thread类和java.lang.Runnable接口。

Thread是线程类,创建一 个Thread对象就会产生一个新的线程。

而线程执行的程序代码是在实现Runnable接口对象的run()方法 中编写的,实现Runnable接口对象是线程执行对象。

线程执行对象实现Runnable接口的run()方法,run()方法是线程执行的入口,该线程要执行程序代码都 在此编写的,run()方法称为线程体。


注意:主线程中执行入口是main(String[] args)方法,这里可以控制程序的流程,管理其他的子线 程等。

子线程执行入口是线程执行对象(实现Runnable接口对象)的run()方法,在这个方法可以 编写子线程相关处理代码。


详细了解java.lang.Thread类和java.lang.Runnable接口见Java官方API文档

http://www.matools.com/api/java8


有以下几种方式创建子线程


(1)通过实现Runnable接口

创建步骤:

1、通过实现Runnable接口创建线程执行类

2、通过重写Runnable中的run方法,编写线程执行代码

3、创建线程Thread对象,将线程执行对象传递给它

4、开始线程


创建线程Thread对象时,可以将线程执行对象传递给它,这需要是使用Thread类如下两个构造方法:


Thread(Runnable target, String name):target是线程执行对象,实现Runnable接口。name为线程指 定一个名字。

Thread(Runnable target):target是线程执行对象,实现Runnable接口。线程名字是由JVM分配 的。

实现Runnable接口的线程执行对象Runner代码如下:

public class Runner implements Runnable {
//    编写执行线程的代码,重写Runnable接口中的run方法,run()方法是线程体,在该方法中编写你自己的线程处理代码。
    @Override
    public void run() {
        for (int i=0;i<10;i++){
//            打印线程次数和线程名
            System.out.printf("第%d次执行--%s\n",i,Thread.currentThread().getName());
            try {
//                随机生成休眠时间
                long sleepTime = (long)(1000*Math.random());
//                线程休眠
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        线程执行结束
        System.out.println("执行完成"+Thread.currentThread().getName());
    }
}

测试代码:

public class HelloWorld {
    public static void main(String[] args) {
//        创建一个线程t1,参数是线程执行对象Runner
        Thread t1 = new Thread(new Runner());
        t1.start();
//        创建一个线程t2,参数是线程执行对象Runner
        Thread t2 = new Thread(new Runner(),"MyThread");
        t2.start();
    }
}

Thread.currentThread()可以获得当前线程对象,getName()是Thread类的实例方法,可以获得线程的名。

Thread.sleep(sleepTime)是休眠当前线程,sleep是静态方法:


static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠。

static void sleep(long millis, int nanos) 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程 休眠。

线程创建完成还 需要调用start()方法才能执行,start()方法一旦调用线程进入可以执行状态, 可以执行状态下的线程等待CPU调度执行,CPU调用后线程进行执行状态,运行run()方法。

运行结果如下:

第0次执行--Thread-0
第0次执行--MyThread
第1次执行--Thread-0
第1次执行--MyThread
第2次执行--MyThread
第2次执行--Thread-0
第3次执行--MyThread
第4次执行--MyThread
第3次执行--Thread-0
第4次执行--Thread-0
第5次执行--MyThread
第6次执行--MyThread
第5次执行--Thread-0
第6次执行--Thread-0
第7次执行--Thread-0
第7次执行--MyThread
第8次执行--Thread-0
第8次执行--MyThread
第9次执行--MyThread
第9次执行--Thread-0
执行完成Thread-0
执行完成MyThread

提示: 仔细分析一下运行结果,会发现两个线程是交错运行的,感觉就像是两个线程在同时运行。但是实际上一台PC通常就只有一颗CPU,在某个时刻只能是一个线程在运行,而Java语言在 设计时就充分考虑到线程的并发调度执行。对于程序员来说,在编程时要注意给每个线程执行的 时间和机会,主要是通过让线程休眠的办法(调用sleep()方法)来让当前线程暂停执行,然后由 其他线程来争夺执行的机会。如果上面的程序中没有用到sleep()方法,则就是第一个线程先执行 完毕,然后第二个线程再执行完毕。所以用活sleep()方法是多线程编程的关键。


(2)通过继承Thread线程类

事实上Thread类也实现了Runnable接口,所以Thread类也可以作为线程执行对象,这需要继承Thread类,覆盖run()方法。


创建步骤:

1、通过继承Thread线程类创建线程执行类

2、定义构造方法,通过super调用父类Thread构造方法

这两个Thread类 构造方法:


Thread(String name):name为线程指定一个名字。

Thread():线程名字是JVM分配的。

3、通过重写Thread中的run方法,编写线程执行代码

4、创建线程执行对象,将参数传递给它

5、开始线程


代码如下:

//线程执行对象
public class MyThread extends Thread {
//    调用了一个构造方法,通过super调用父类的构造方法
    public MyThread(){
        super();
    }
    public MyThread(String name){
        super(name);
    }
//   编写执行线程代码
//    重写Threa的的run方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            //            打印线程次数和线程名
            System.out.printf("第%d次执行--%s\n", i,getName());
            try {
                //                随机生成休眠时间
                long sleepTime = (long) (1000 * Math.random());
                //                线程休眠
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //        线程执行结束
        System.out.println("执行完成" + getName());
    }
}

测试代码:

public class HelloWorld {
    public static void main(String[] args) {
//        创建线程t1
        Thread t1 = new MyThread();
//        开始线程t1
        t1.start();
        //        创建线程t1
        Thread t2 = new MyThread("mythread");
//        开始线程t1
        t2.start();
    }
}

由于Java只支持单重继承,继承Thread类的方式不能再继承其他父类。当开发一些图形界 面的应用时,需要一个类既是一个窗口(继承JFrame)又是一个线程体,那么只能采用实现 Runnable接口方式。


(3)使用匿名内部类和Lambda表达式实现线程体

如果线程体使用的地方不是很多,可以不用单独定义一个类。可以使用匿名内部类或Lambda表达式直接实现Runnable接口。Runnable中只有一个方法是函数式接口,可以使用Lambda表达式。

代码如下:

//使用匿名内部类和Lambda表达式实现线程体
public class HelloWorld {
    public static void main(String[] args) {
//    创建线程t1,参数是实现Runnable接口的匿名内部类
        Thread t1 = new Thread(new Runnable() {
//            编写执行线程代码
            @Override
            public void run() {
                for (int i=0;i<10;i++){
//            打印线程次数和线程名
                    System.out.printf("第%d次执行--%s\n",i,Thread.currentThread().getName());
                    try {
//                随机生成休眠时间
                        long sleepTime = (long)(1000*Math.random());
//                线程休眠
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //        线程执行结束
                System.out.println("执行完成"+Thread.currentThread().getName());
            }
        });
//        开始线程t1
//        创建线程t2,参数是实现Runnable接口的Lambda表达式
        Thread t2 = new Thread(() -> { //重写run方法
            for (int i=0;i<10;i++){
//            打印线程次数和线程名
                System.out.printf("第%d次执行--%s\n",i,Thread.currentThread().getName());
                try {
//                随机生成休眠时间
                    long sleepTime = (long)(1000*Math.random());
//                线程休眠
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //        线程执行结束
            System.out.println("执行完成"+Thread.currentThread().getName());
        },"MyThread");
        t2.start();
    }
}

匿名内部类和Lambda表达式不需要定义一个线程类文件,使用起来很方便。特别是 Lambda表达式使代码变得非常简洁。



相关文章
|
1天前
|
Java 关系型数据库 MySQL
如何用java的虚拟线程连接数据库
本文介绍了如何使用Java虚拟线程连接数据库,包括设置JDK版本、创建虚拟线程的方法和使用虚拟线程连接MySQL数据库的示例代码。
14 6
如何用java的虚拟线程连接数据库
|
1天前
|
Oracle Java 关系型数据库
Java编程之旅:从基础到进阶
Java,一种广泛使用的编程语言,因其平台无关性、面向对象的特性而备受推崇。本文旨在通过简明易懂的语言和实际代码示例,引导初学者了解Java的基本概念,并逐步深入到更复杂的编程技巧。我们将从Java的安装开始,经过变量、数据类型、控制结构等基础知识的学习,最后探讨异常处理和文件操作等进阶话题。无论你是编程新手还是有一定经验的开发者,这篇文章都将为你提供有价值的参考和启示。
|
19小时前
|
Java 调度
Java一个线程的生命周期详解
Java中,一个线程的生命周期分为五个阶段:NEW(新建),RUNNABLE(可运行),BLOCKED(阻塞),WAITING(等待),TERMINATED(终止)。线程创建后处于新建状态,调用start方法进入可运行状态,执行中可能因等待资源进入阻塞或等待状态,正常完成或异常终止后进入终止状态。各状态间可相互转换,构成线程的生命周期。
|
1天前
|
Java 开发者
农行1面:Java如何保证线程T1,T2,T3 顺序执行?
本文探讨了如何保证线程T1、T2、T3的顺序执行,这是农行面试中的一道题目,旨在考察候选人对多线程基础、同步机制、线程间通信及Java并发包的掌握情况。文章详细介绍了六种方法:`join()`、`CountDownLatch`、`Semaphore`、单线程池、`synchronized` 和 `CompletableFuture`,并通过示例代码展示了每种方法的具体实现。这些方法不仅适用于面试备考,还能帮助开发者更好地理解和掌握线程同步技术。
20 2
|
19小时前
|
Java API 调度
Java 多线程编程详解
《Java多线程编程详解》深入浅出地讲解了Java平台下的多线程核心概念、API使用及最佳实践。从基础理论到实战案例,本书帮助读者掌握并发编程技巧,提升软件开发中的效率与性能,是Java开发者不可或缺的参考指南。
|
1天前
|
NoSQL Java Redis
Reactor实战,创建一个简单的单线程Reactor(理解了就相当于理解了多线程的Reactor)
本文通过一个简单的单线程Reactor模式的Java代码示例,展示了如何使用NIO创建一个服务端,处理客户端的连接和数据读写,帮助理解Reactor模式的核心原理。
6 0
Reactor实战,创建一个简单的单线程Reactor(理解了就相当于理解了多线程的Reactor)
|
21小时前
|
设计模式 Java 物联网
【多线程-从零开始-玖】内核态,用户态,线程池的参数、使用方法详解
【多线程-从零开始-玖】内核态,用户态,线程池的参数、使用方法详解
8 0
|
21小时前
|
Java 调度
【多线程-从零开始-贰】线程的构造方法和常见属性
【多线程-从零开始-贰】线程的构造方法和常见属性
10 0
|
2月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
70 1
|
12天前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口