【多线程】初识线程,基础了解

目录

认识线程

    概念

        什么是线程?

        为啥要有线程

        进程和线程的区别

        Java 的线程 和 操作系统线程 的关系

    创建线程

        1.继承 Thread 类

        2.实现 Runnable 接口

        3.通过匿名内部类方式创建Thread与实现Runnable

       4.Lmabda表达式

Thread 类及常见方法

    Thread 的常见构造方法

     Thread 的几个常见属性

    启动一个线程-start()

    中断一个线程

    等待一个线程-join()

     获取当前线程引用

    休眠当前线程

线程的状态

    观察线程的所有状态


认识线程

    概念

        什么是线程?

一个线程就是一个 " 执行流 ". 每个线程之间都可以按照顺讯执行自己的代码 . 多个线程之间 " 同时 " 执行着多份代码。
多进程可以处理一个很大或复杂的任务,但启动进程时申请内存,申请文件资源,进程结束时释放文件,释放内存的操作非常耗时。
为了解决资源消耗问题,提出一个轻量化进程的概念(线程),创建线程时只关注要处理的任务,使用的是进程创建时申请的所有资源(类比开工厂)。
例:进程就相当于开工厂新建一个场子,需要购买地皮、拉电拉水、修建仓库等,非常的费时间;线程就相当于在原有工厂资源的基础上重新开一条生产线,不需要前期的资源申请,节省时间,更好的利用资源。

        为啥要有线程

首先 , " 并发编程 " 成为 " 刚需 ".
  • 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU资源.
  • 有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编程.
其次 , 虽然多进程也能实现 并发编程 , 但是线程比进程更轻量 .
  • 创建线程比创建进程更快.
  • 销毁线程比销毁进程更快.
  • 调度线程比调度进程更快.
最后 , 线程虽然比进程轻量 , 但是人们还不满足 , 于是又有了 " 线程池 "(ThreadPool) " 协程 "
(Coroutine)

        进程和线程的区别

  • 进程是包含线程的. 每个进程至少有一个线程存在,即主线程。
  • 进程和进程之间不共享内存空间. 同一个进程的线程之间共享同一个内存空间.
  • 进程是系统分配资源的最小单位,线程是系统调度的最小单位。

        Java 的线程 和 操作系统线程 的关系

线程是操作系统中的概念 . 操作系统内核实现了线程这样的机制 , 并且对用户层提供了一些 API 供用户使用( 例如 Linux pthread ).
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装 .

    创建线程

        1.继承 Thread

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

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("这是线程运行代码");
    }
}

        2.实现 Runnable 接口

public class MRunnable {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("这是线程运行代码");
    }
}

使用Runnable定义的好处:

  • 解耦,把定义线程与定义任务分开,以便修改代码统一修改
对比上面两种方法 :
  • 继承 Thread , 直接使用 this 就表示当前线程对象的引用.
  • 实现 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用 Thread.currentThread()

        3.通过匿名内部类方式创建Thread与实现Runnable

Thread

public class Thread_Anon {
    public static void main(String[] args) {
        Thread thread = new Thread(){
            @Override
            public void run() {
                System.out.println("这是线程运行代码");
            }
        };
        thread.start();
    }
}

Runnable

public class Runnable_Anon {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("这是线程执行代码");
            }
        });
        thread.start();
    }
}

       4.Lmabda表达式

public class lambda {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("这是线程执行代码");
        });
        thread.start();
    }
}


用Lambda表达式实现的接口必须是函数式接口(接口中只有一个没有实现的方法)
使用多线程编程主要是为了充分利用CPU资源,提升程序运行效率;
但并不是所有场景使用多线程都可以提高效率

Thread 类及常见方法

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
每个执行流,也需要有一个对象来描述,而 Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。

    Thread 的常见构造方法

     Thread 的几个常见属性

属性
获取方法
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()
  • ID 是线程的唯一标识,不同线程不会重复
  • 名称是各种调试工具用到
  • 状态表示线程当前所处的一个情况
  • 优先级高的线程理论上来说更容易被调度到
  • 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
  • 是否存活,即简单的理解,为 run 方法是否运行结束了
  • 线程的中断问题

    启动一个线程-start()

之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。
  覆写 run 方法是提供给线程要做的事情的指令清单
  线程对象可以认为是把 李四、王五叫过来了
  而调用 start() 方法,就是喊一声: 行动起来! ,线程才真正独立去执行了。
调用 start 方法 , 才真的在操作系统的底层创建出一个线程 .

    中断一个线程

李四一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加一些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如何通知李四停止呢?这就涉及到我们的停止线程的方式了。
目前常见的有以下两种方式:
  1. 通过共享的标记来进行沟通
  2. 调用 interrupt() 方法来通知
public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            // 两种方法均可以
            while (!Thread.interrupted()) {
                //while (!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName()
                            + ": 有内鬼,终止交易!");
                    // 注意此处的 break
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName()
                    + ": 啊!险些误了大事");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName()
                + ": 让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
        thread.interrupt();
    }
}

 thread 收到通知的方式有两种:

1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通
知, 清除中断标志
  • 当出现 InterruptedException 的时候 , 要不要结束线程取决于 catch 中代码的写法 . 可以选择
    忽略这个异常 , 也可以跳出循环结束线程 .
2. 否则,只是内部的一个中断标志被设置, thread 可以通过
  • Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
  • Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志

    等待一个线程-join()

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。

     获取当前线程引用

方法
说明
public static Thread currentThread();
返回当前线程对象的引用

    休眠当前线程

因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。
方法
说明
public static void sleep(long millis) throws InterruptedException
休眠当前线程 millis
毫秒
public static void sleep(long millis, int nanos) throws
InterruptedException
可以更高精度的休眠

线程的状态

    观察线程的所有状态

线程的状态是一个枚举类型 Thread.State
public class ThreadState {
    public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()) {
            System.out.println(state);
        }
    }
}
  • NEW: 安排了工作, 还未开始行动
  • RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.
  • BLOCKED: 这几个都表示排队等着其他事情
  • WAITING: 这几个都表示排队等着其他事情
  • TIMED_WAITING: 这几个都表示排队等着其他事情
  • TERMINATED: 工作完成了.

这里只是线程的部分知识,剩下的知识总结我会后面继续发布,期待大家关注

猜你喜欢

转载自blog.csdn.net/m0_59952648/article/details/130557051