【JavaEE初阶】线程的概念与创建

简介: 【JavaEE初阶】线程的概念与创建



本节目标

  • 认识多线程
  • 创建多线程

🌲线程的概念

🚩线程是什么

Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

一个线程就是一个 “执行流”. 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 “同时” 执行着多份代码。

举个例子:

一家公司要去银行办理业务,既要进行财务转账,又要进行福利发放,还得进行缴社保。

如果只有张三一个会计就会忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三又找来两位同事李四、王五一起来帮助他,三个人分别负责一个事情,分别申请一个号码进行排队,自此就有了三个执行流共同完成任务,但本质上他们都是为了办理一家公司的业务。

此时,我们就把这种情况称为多线程,将一个大任务分解成不同小任务,交给不同执行流就分别排队执行。其中李四、王五都是张三叫来的,所以张三一般被称为主线程(Main Thread)

🚩为啥要有线程

首先, “并发编程” 成为 “刚需”.

  • 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU
    资源.
  • 有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编
    程.

其次, 虽然多进程也能实现 并发编程, 但是线程比进程更轻量.

  • 创建线程比创建进程更快.
  • 销毁线程比销毁进程更快.
  • 调度线程比调度进程更快.

最后, 线程虽然比进程轻量, 但是人们还不满足, 于是又有了 “线程池”(ThreadPool) 和 “协程”(Coroutine)

关于线程池和协程博主会在后面一一介绍.此处暂时不做过多讨论.

🚩进程和线程的区别

  • 进程是包含线程的. 每个进程至少有一个线程存在,即主线程
  • 进程和进程之间不共享内存空间. 同一个进程的线程之间共享同一个内存空间.

比如之前的多进程例子中,每个客户来银行办理各自的业务,但他们之间的票据肯定是不想让别人知道的,否则钱不就被其他人取走了么。

而上面我们的公司业务中,张三、李四、王五虽然是不同的执行流,但因为办理的都是一家公司的业务,所以票据是共享着的。这个就是多线程和多 进程的最大区别。

  • 进程是系统分配资源的最小单位,线程是系统调度的最小单位

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

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库).

Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装

😎第一个多线程程序

感受多线程程序和普通程序的区别:

  • 每个线程都是一个独立的执行流
  • 多个线程之间是 “并发” 执行的

程序代码如下:

class MyThread extends Thread {
    @Override
    public void run() {
        while(true) {
            System.out.println("hello thread");
            //休眠一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class TestMian_1 {
    public static void main(String[] args) {
        Thread t1 = new MyThread();
        t1.start();
        while (true) {
            System.out.println("hello main");
            //休眠一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

以上就是一个简单的多线程的程序

start:真正的创建一个线程(从系统里创建)

MyThread里面的run:描述线程需要干啥

如果按照我们以前的思维,因为我们写的是个死循环,所以应该执行一个语句,但是这是多线程,执行结果如下:

结果显示交替进行,这就展现出了多线程与普通程序的区别

🚩使用 jconsole 命令观察线程

我们可以使用jdk自带的工具 jconsole查看当前Java进程中所有的线程

操作流程如下:

  1. 第一步,找到jdk
  2. 第二步,点进去,找到里面的bin文件点进去
  3. 第三步,点击bin文件夹里面的jconsole
  4. 第四步,找到你所创建进程
  5. 第五步,遇到以下情况,直接点击不安全连接就好
  6. 第六步,点击线程进行查看

🎍创建线程

🚩方法一:继承 Thread 类

  1. 继承 Thread 来创建一个线程类
class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println("这里是线程运行的代码");
  }
}
  1. 创建 MyThread 类的实例
MyThread t = new MyThread();
  1. 调用 start 方法启动线程
t.start(); // 线程开始运行

🚩方法2:实现 Runnable 接口

  1. 实现 Runnable 接口
class MyRunnable implements Runnable {
  @Override
  public void run() {
    System.out.println("这里是线程运行的代码");
  }
}
  1. 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数
Thread t = new Thread(new MyRunnable());
  1. 调用 start 方法
t.start(); // 线程开始运行

🚩方法1、2对比

对比上面两种方法:

  • 继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
  • 实现 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用Thread.currentThread()

🚩方法3:其他变形

  • 匿名内部类创建 Thread 子类对象
// 使用匿名类创建 Thread 子类对象
Thread t1 = new Thread() {
  @Override
  public void run() {
    System.out.println("使用匿名类创建 Thread 子类对象");
  }
};
  • 匿名内部类创建 Runnable 子类对象
// 使用匿名类创建 Runnable 子类对象
Thread t2 = new Thread(new Runnable() {
  @Override
  public void run() {
    System.out.println("使用匿名类创建 Runnable 子类对象");
  }
});
  • lambda 表达式创建 Runnable 子类对象
// 使用 lambda 表达式创建 Runnable 子类对象
Thread t3 = new Thread(() -> System.out.println("使用匿名类创建 Thread 子类对象"));
Thread t4 = new Thread(() -> {
System.out.println("使用匿名类创建 Thread 子类对象");
});

🎋多线程的优势

增加运行速度

可以观察多线程在一些场合下是可以提高程序的整体运行效率的。

  • 使用 System.nanoTime() 可以记录当前系统的 纳秒 级时间戳.
  • serial 串行的完成一系列运算. concurrency 使用两个线程并行的完成同样的运算
public class ThreadAdvantage {
    // 多线程并不一定就能提高速度,可以观察,count 不同,实际的运行效果也是不同的
    private static final long count = 10_0000_0000;
    public static void main(String[] args) throws InterruptedException {
        // 使用并发方式
        concurrency();
        // 使用串行方式
        serial();
    }
    private static void concurrency() throws InterruptedException {
        long begin = System.nanoTime();
        // 利用一个线程计算 a 的值
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                int a = 0;
                for (long i = 0; i < count; i++) {
                    a--;
                }
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                int b = 0;
                for (long i = 0; i < count; i++) {
                    b--;
                }
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        // 统计耗时
        long end = System.nanoTime();
        double ms = (end - begin) * 1.0 / 1000 / 1000;
        System.out.printf("并发: %f 毫秒%n", ms);
    }
    private static void serial() {
        // 全部在主线程内计算 a、b 的值
        long begin = System.nanoTime();
        int a = 0;
        for (long i = 0; i < count; i++) {
            a--;
        }
        int b = 0;
        for (long i = 0; i < count; i++) {
            b--;
        }
        long end = System.nanoTime();
        double ms = (end - begin) * 1.0 / 1000 / 1000;
        System.out.printf("串行: %f 毫秒%n", ms);
    }
}

运行结果如下:

⭕总结

关于《【JavaEE初阶】线程的概念与创建》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

相关文章
|
7天前
|
Java
【JavaEE】——多线程常用类
Callable的call方法,FutureTask类,ReentrantLock可重入锁和对比,Semaphore信号量(PV操作)CountDownLatch锁存器,
|
7天前
|
Java 关系型数据库 MySQL
【JavaEE“多线程进阶”】——各种“锁”大总结
乐/悲观锁,轻/重量级锁,自旋锁,挂起等待锁,普通互斥锁,读写锁,公不公平锁,可不可重入锁,synchronized加锁三阶段过程,锁消除,锁粗化
|
7天前
|
Java Go 调度
【JavaEE】——线程池大总结
线程数量问题解决方式,代码实现线程池,ThreadPoolExecutor(核心构造方法),参数的解释(面试:拒绝策略),Executors,工厂模式,工厂类
|
7天前
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
7天前
|
Java 调度
|
7天前
|
Java 调度
【JavaEE】——线程的安全问题和解决方式
【JavaEE】——线程的安全问题和解决方式。为什么多线程运行会有安全问题,解决线程安全问题的思路,synchronized关键字的运用,加锁机制,“锁竞争”,几个变式
|
7天前
|
Java API 调度
【JavaEE】——多线程(join阻塞,计算,引用,状态)
【JavaEE】——多线程,join,sleep引起的线程阻塞,多线程提升计算效率,如何获取线程的引用和状态
|
7天前
|
Java 程序员 调度
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
创建线程的五种方式,Thread常见方法(守护进程.setDaemon() ,isAlive),start和run方法的区别,如何提前终止一个线程,标志位,isinterrupted,变量捕获
|
7天前
|
安全 Java API
【JavaEE】多线程编程引入——认识Thread类
Thread类,Thread中的run方法,在编程中怎么调度多线程
|
7天前
|
安全 前端开发 Java
【JavaEE】——线程的诞生(超详细、易理解)
进程对内存的管理,进程间的通信,进程的缺点,线程的概念和特点,进程和线程在内存中的分配方式,进程和线程的结合,进程和线程之间的关系,线程的缺点,有无线程的对比,线程的总结