Java基础之多线程之原理、实现方式及匿名内部类创建线程方法

一、概念

进程:操作系统当中正在执行的一个程序。例如正在运行一个QQ。
线程:进程之内多任务的执行单位。例如迅雷当中正在下载的多个电影。
JVM当中:栈(Stack)内存是线程独立的,堆(Heap)内存是线程共享的。
(1)Java程序运行的时候至少有两个线程:
1)主线程(main方法执行的所在线程)
2)垃圾回收线程(gc线程)
(2)Java当中的多线程采用抢占式调度:
多个线程之间无规则来回抢CPU,谁抢到了谁执行,谁没抢到活该,下次再抢。
无法精确控制多个线程之间来回切换的规律。

二、多线程原理

阅读以下程序:

// 自定义实现类
public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("小强-" + i);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// main方法
public class Demo01Thread {

    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("旺财-" + i);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

程序流程图如下:
这里写图片描述
线程开启的内存图如下:
这里写图片描述

三、如何实现多线程的程序

  1. 继承类:java.lang.Thread类。
    Thread类代表线程类,一个Thread对象就是一个线程,也就是一个任务执行的单位。
  2. 实现Runnable接口。
    :线程是操作系统当中极其宝贵的系统资源,一定要节省使用。

四、使用步骤

(一)继承Thread类
1. 定义一个类,继承Thread类。
2. 覆盖重写其中的run方法,用来指定线程任务内容。
public void run() {…}
3. 创建对象,调用.start()方法启动线程。
:不要自己调用run,否则没有多线程的效果。
tips:
1. 可以通过下面的方法来获取当前线程的名称:
String name = Thread.currentThread().getName();
线程的命名如果没有指定,那么将会默认使用Thread-0、Thread-1进行依次命名。
2. 在run()方法中如果出现了异常只能通过try-catch来捕捉,这是因为Thread并未继承任何异常类,子类对异常的处理范围又必须小于父类,所以无法throws异常。

// 自定义线程类,继承Thread
public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("小强-" + i);
        }
    }
}
// main方法
public class Demo01Thread {

    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
    }

}

(二)实现Runnable接口
1. 定义一个类实现Runnable接口。
2. 覆盖重写run()方法,指定任务内容。
3. 创建Runnable对象作为Thread的构造参数。
4. 调用start()方法,启动线程。

// 自定义实现类,实现Runnable接口
public class MyRunnableImpl implements Runnable {
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        for (int i = 1; i <= 100; i++) {
            System.out.println(name + " : " + i);
        }
    }
}
public class Demo04Runnable {

    public static void main(String[] args) {
        Runnable task = new MyRunnableImpl(); // 创建了Runnable实现类的对象。

        new Thread(task).start(); // 没有定义线程名称,自动分配名称
        new Thread(task, "我的线程").start(); // 指定了自定义的线程名称,用指定的。
    }

}

:推荐使用接口形式,实现Runnable接口比继承Thread类所具有的优势:
1. 适合多个相同的程序代码的线程去共享同一个资源。
2. 可以避免java中的单继承的局限性,但实现接口就没有限制。
3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4. 线程池只能放入实现Runable或Callable接口线程,不能直接放入继承Thread的类。
5. Thread中功能很多,但Runnable中只有一个run()方法,使用起来更加简单。

五、使用匿名内部类实现线程的创建

// 使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法:
public class NoNameInnerClassThread {
public static void main(String[] args) {
        // new Runnable(){
        // public void run(){
        // for (int i = 0; i < 20; i++) {
        // System.out.println("张宇:"+i);
        // }
        // }
        // }; //‐‐‐这个整体 相当于new MyRunnable()
        Runnable r = new Runnable(){
            public void run(){
                for (int i = 0; i < 20; i++) {
                System.out.println("张宇:"+i);
                }
            }
        };
        new Thread(r).start();
        for (int i = 0; i < 20; i++) {
            System.out.println("费玉清:"+i);
        }
    }
}

匿名类的三种使用场景:
1. 可以适用于接口,覆盖重写抽象方法。
2. 也可以适用于抽象类,覆盖重写抽象方法。
3. 也可以适用于普通类,只要不是final的,就能覆盖重写。

猜你喜欢

转载自blog.csdn.net/sunshinegirl168660/article/details/81516120