一篇搞懂Java多线程(全网最细)(一)

简介: 一篇搞懂Java多线程(全网最细)(一)

前言

DOS系统有一个非常明显的特点,只要一中病毒之后系统就会立刻死记,因为传统的DOS系统是采用单进程的处理方式,所以只能有一个程序独自运行,其他程序无法运行。在Windows系统中,即使出现了病毒,系统照样可以正常使用,因为在Windows中采用的是多进程的处理方式,那么在同一个时间段上会有多个程序同时运行。线程实际上就是在进程的基础上的进一步划分,如果一个进程都没有,则线程肯定会消失;而如果线程消失了,进程未必会消失。而且,所有的线程都是在进程的基础上并发运行的(同时运行)。

一、线程的基本介绍

多线程:相当于老板请员工来帮我做事。

1. 中央处理器(CPU

       CPU的中文名称是中央处理器,是进行逻辑运算用的,主要由运算器、控制器、寄存器三部分组成,从字面意思看运算就是起着运算的作用,控制器就是负责发出CPU每条指令所需要的信息,寄存器就是保存运算或指令的一些临时文件,这样可以保证更高的速度,也就是我们的线程运行在CPU之上。

  • 单核 :单核的CPU是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。同时间段内有几个多线程需要CPU去运行时,CPU也只能交替去执行多个线程中的一个线程,但是由于其执行速度特别快,因此感觉不出来。
  • 多核 :多核的CPU才能更好的发挥多线程的效率。

2. 程序

开发写的代码称之为程序。程序就是一堆代码·,一组数据和指令集,是一个静态的概念。

3. 进程(Process)

  • CPU从硬盘中读取一段程序到内存中,该执行程序的实例就叫做进程
  • 一个程序如果被CPU多次被读取到内存中,变成多个独立的进程。将程序运行起来,我们称之为进程。进程是执行程序的一次执行过程,他是动态的概念。进程存在生命周期,也就是说程序随着程序的终止而销毁。进程之间是通过TCP/IP端口实现交互的。

简单的理解:一个应用程序(一个进程就是一个软件),一个程序至少包含一个进程,一个进程中至少包含一条线程;

4. 线程

  • CPU处理数据时,某一个时刻点任何CPU都只能处理一个程序。
  • 线程是进程中的实际运作的单位,是进程的一条流水线,是程序的实际执行者,是最小的执行单位。通常在一个进程中可以包含若干个线程。线程是CPU调度和执行的最小单位
  • 一个进程可以有多个线程,如视频可以同时看图像、听声音、看弹幕,等等;
  • 很多线程都是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器,如果是模拟出来的多线程,即一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换很快,所以就有同时执行的错觉。

对于java程序来说,当在DOS命令窗口中输入:

  1. java HelloWorld 回车之后。会先启动JVM,而JVM就是一个进程。
  2. JVM再启动一个主线程调用main方法(main方法就是主线程)。
  3. 同时再启动一个垃圾回收线程负责看护,回收垃圾。

注意 :使用多线程机制之后,main方法结束只是主线程结束了,其他线程还没结束,但没有主线程也不能运行。最起码,现在的java程序中至少有两个线程并发,一个是 垃圾回收线程,一个是 执行main方法的主线程。

5. 进程与线程的关系

  • 进程 :可以看做是现实生活当中的公司。
  • 线程 :可以看做是公司当中的某个员工。

注意 :进程A和进程B的内存独立不共享。多线程开发

6. 多线程开发

6.1 并发

       同一对象被多个线程同时操作;(这是一种假并行。即一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错觉)。

       特点 :同时安排若干个任务,这些任务可以彼此穿插着进行;有些任务可能是并行的,比如买菜、发邮件和去洗脚的某些路是重叠的,这时你的确同时在做三件事;但进菜市场和发邮件和接娃三者是互斥的,每个时刻只能完成其中一件。换句话说,并发允许两个任务彼此干扰。

6.2 并行

你(线程)做你的事,我(线程)做我的事,咱们互不干扰并同时进行。

6.3 串行

一个程序处理当前进程,按顺序接着处理下一个进程,一个接着一个进行

特点 : 前一个任务没搞点,下一个任务就只能等着。

7. 多线程的优点

  1. 提高应用程序的响应。堆图像化界面更有意义,可以增强用户体验。
  2. 提高计算机系CPU的利用率
  3. 改善程序结构,将即长又复杂的进程分为多个线程,独立运行,利于理解和修改。

7.1 何时需要多线程

  1. 程序需要同时执行两个或多个任务
  2. 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
  3. 需要一些后台运行的程序时。

二、线程的创建和启动

1. 多线程实现的原理

       Java语言的JVM允许程序运行多个线程,多线程可以通过java中的java.lang.Thread类来体现。

Thread特性:

  • 每个线程多事通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体。
  • 通过Thread方法的start()方法来启动这个线程,而非直接调用run()。

2. 线程的创建及注意事项

第一种方式:继承Thread类

  1. 创建一个继承Thread类的子类
  2. 重写Thread类的run()方法
  3. 创建Thread类的子类的对象
  4. 通过此对象调用start()来启动一个线程
package com.thread;
/**
 * 我的线程
 * 
 * @author 云村小威
 *
 * @2023年7月21日 下午12:02:44
 */
public class MyThread extends Thread {
  @Override
  public void run() {
    // 编写程序,这段程序运行在分支线程中
  }
}
package com.thread;
/**
 * 
 * @author 云村小威
 *
 * @2023年7月21日 下午12:37:53
 */
public class Text01 {
  public static void main(String[] args) {
    MyThread t = new MyThread();
    // 启动线程
    t.start();
    // run方法不会启动线程,不会分配新的分支栈(这种方式就是单线程)
    t.run();
  }
}

注意 :

  • t.run()不会启动线程,只是普通的调用方法而已。不会分配新的分支栈。(这种方法就是单栈程)
  • t.start()方法的作用是 :启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。

       这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。

package com.thread;
/**
 * 我的线程
 * 
 * @author 云村小威
 *
 * @2023年7月21日 下午12:02:44
 */
public class MyThread extends Thread {
  private String name;
  public MyThread(String name) {
    this.name=name;
  }
  @Override
  public void run() {
    // 编写程序,这段程序运行在分支线程中
    for (int i = 0; i < 5; i++) {
      System.out.println(name+"运行:"+i);
      //休眠
      try {
        sleep((int)Math.random()*10);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}
package com.thread;
/**
 * 
 * @author 云村小威
 *
 * @2023年7月21日 下午12:37:53
 */
public class Text01 {
  public static void main(String[] args) {
    MyThread t1 = new MyThread("A");
    MyThread t2 = new MyThread("B");
    // 启动线程
    t1.start();
    t2.start();
  }
}

运行:

注意 :

  1. start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的。
  2. 从程序运行的结果可以发现,多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。
  3. Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。
  4. 实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。
  5. 但是start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。

3. Thread类的常用方法

Start() 启动当前线程的run()方法
run() 通常需要重写Thread类的此方法,将创建的线程要执行的操作声明在此方法中
currentThread() 静态方法,返回当前代码执行的线程
getName() 获取当前线程的名字
setName() 设置当前线程的名字
yield() 暂停当前正在执行的线程对象,并执行其他线程(释放当前CPU的执行权)
join() 等待该线程终止的时间(millis毫秒),再可执行其他线程
stop() 已过时,当执行此方法时,强制结束当前线程
sleep(long millitime) 让线程睡眠指定的毫秒数,在指定时间内,线程是阻塞状态
isAlive() 判断当前线程是否处于活动状态
setPriority() 更改线程的优先级
setDaemon() 将该线程标记为守护线程或用户线程

4. 实现java.lang.Runnable接口

第二种方式:实现java.lang.Runnable接口

步骤同继承Thread类;

package com.thread;
/**
 * 通过实现Runnable接口创建线程
 * 
 * @author 云村小威
 *
 * @2023年7月21日 下午12:02:44
 */
public class MyThread2 implements Runnable {
  private String name;
  public MyThread2(String name) {
    this.name=name;
  }
  @Override
  public void run() {
    // 编写程序,这段程序运行在分支线程中
    for (int i = 0; i < 5; i++) {
      System.out.println(name+"运行:"+i);
      //休眠
      try {
        Thread.sleep((int)Math.random()*10);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}
package com.thread;
/**
 * 
 * @author 云村小威
 *
 * @2023年7月21日 下午12:37:53
 */
public class Text02 {
  public static void main(String[] args) {
    new Thread(new MyThread2("A")).start();
    new Thread(new MyThread2("B")).start();
  }
}

运行:

相关文章
|
9天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
11天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
11天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
11天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
35 3
|
11天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
93 2
|
19天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
46 6
|
1月前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
28天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
28天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
50 3
|
29天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####