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

一、基础知识

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

(1)进程

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

(2)线程

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

(3)主线程

Java程序至少会有一个线程,这就是主线程,程序启动后是由JVM创建主线程,程序结束时由JVM停 止主线程。主线程它负责管理子线程,即子线程的启动、挂起、停止等等操作。图所示是进程、 主线程和子线程的关系,其中主线程负责管理子线程,即子线程的启动、挂起、停止等操作。
在这里插入图片描述
获取主线程示例代码如下:

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-00次执行--MyThread
第1次执行--Thread-01次执行--MyThread
第2次执行--MyThread
第2次执行--Thread-03次执行--MyThread
第4次执行--MyThread
第3次执行--Thread-04次执行--Thread-05次执行--MyThread
第6次执行--MyThread
第5次执行--Thread-06次执行--Thread-07次执行--Thread-07次执行--MyThread
第8次执行--Thread-08次执行--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表达式使代码变得非常简洁。

以上内容仅供参考学习,如有侵权请联系我删除!
如果这篇文章对您有帮助,左下角的大拇指就是对博主最大的鼓励。
您的鼓励就是博主最大的动力!

发布了69 篇原创文章 · 获赞 7 · 访问量 3326

猜你喜欢

转载自blog.csdn.net/weixin_45366499/article/details/104346644