概念
多线程概念
- 进程(process):程序的运行实例,是程序向操作系统申请资源(比如内存空间和文件句柄)的基本单位;
 - 线程(Thread):
- 进程中可独立执行的单位;
 - 一个进程可以包含多个线程;
 - 同一进程中的所有线程共享该进程中的资源,如内存空间,文件句柄等;
 
 - 进程中可独立执行的单位;
 - 线程分类:JVM用户线程与内核线程是1比1的关系
- 用户线程:不需要操作系统管理,自行创建,优点是性能更好,缺点是无法充分利用多核CPU的优势,且多线程并不是真正意义上的多线程,而是受CPU内核时间片管理的影响
 - 内核线程:由操作系统管理的线程,能够充分利用多CPU的特点,缺点是线程切换耗时
 
 - 用户线程:不需要操作系统管理,自行创建,优点是性能更好,缺点是无法充分利用多核CPU的优势,且多线程并不是真正意义上的多线程,而是受CPU内核时间片管理的影响
 - 任务:线程要完成的计算就被称为任务,特定的线程总是在执行着特定的任务
 
串行、并行、并发

- 串行:多个任务依次执行
 - 并发(Concurrent):先做任务A,然后A准备就绪,开始等待;在等待A完成的时候,开始准备任务B,任务B准备就绪后,开始等待;在等待任务B完成时开始任务C的准备,然后等待3个任务的完成
 - 并行(Parallel):多任务同时进行,总耗时由需要时间最长的任务
 
编程范式
- 函数式编程(Functional Programming):函数式基本抽象单位;
 - 面向对象编程:类是基本抽象单位;
 - 多线程编程:以线程为基本抽象单位的一种编程范式(Paradigm);
 
线程创建、启动与相关属性
线程创建
Java标准库类 java.lang.Thread 就是Java平台对线程的实现。Thread类或其子类的一个实例就是一个线程。
线程创建就是创建一个Thread类或其子类的实现,线程的任务逻辑放在Thread的run()方法中。Thread常用构造器:Thread() 与 Thread(Runnable target)
创建线程的三种方式:
1、继承 Thread 类
public class Task extends Thread {@Overridepublic void run() {System.out.println("任务执行!");}public static void main(String[] args) {Task task = new Task();task.start();}}
2、实现 Runnable 接口
public class Task implements Runnable{@Overridepublic void run() {System.out.println("任务执行!");}public static void main(String[] args) {Thread task = new Thread(new Task());task.start();}}
3、实现 Callable
线程启动
通过 Thread 类的 start() 方法启动相应线程,实质是请求 java 虚拟机运行相应的线程,该线程何时运行由线程调度器 Scheduler(操作系统一部分)决定。start() 方法调用结束不意味相应线程开始运行。
注意:
- Thread.start():方法会调用本地方法,创建新线程执行 run() 方法
 - Thread.run():方法是在当前线程中执行 run() 方法,不会创建新线程【实现线程池的关键方法】
 
线程属性
常见的线程属性有如下几个:
- 编号,ID
 - 名称,Name
 - 线程类别,Daemon
 - 优先级,Priority
 
1、线程ID:线程ID为 long 类型,用于标识不同的线程,该属性为只读属性
获取线程 ID 的方法:
// 获取当前线程的IDlong id = Thread.currentThread().getId();// 获取某个指定线程的IDThread thread = new Thread();long id = thread.getId();
线程 ID 相关细节:
- 某个编号的线程运行结束后,该编号可能会被后续创建的线程使用;
 - 线程编号只在 Java 虚拟机中的一次运行有效,即某个线程运行时的编号在 JVM 重启后有可能发生改变,因此线程 ID 不适合作为唯一标识;
 
2、线程名称 name
- String 类型,默认的格式为:”Thread-线程ID“,属于可以自定义的属性
 - 为了方便调试,尽量设置易懂的线程名 ```java // 在创建线程时设置线程name Thread thread = new Thread(“线程名”);
 
// 设置当前线程的线程名 Thread.currentThread().setName(“线程名”);
// 设置指定线程的线程名 Thread thread = new Thread(); thread.setName(“新的线程名”);
// 获取线程名 Thread.currentThread().getName();
3、线程类别 Daemon:类型 boolean,值为 true 表示相关线程为守护线程,否则表示相关线程为非守护线程,该属性的默认值与相应线程的父线程的该属性的值相同,该值可以自定义<br />注意事项:1. 该属性必须在相应线程启动之前设置,即对 setDaemon 方法的调用必须在对 start 方法调用之前,否则 setDaemon 方法会抛出 IllegalThreadStateException 异常1. 通过方法 Thread.currentThread().isDaemon() 可以获取当前线程是否为守护线程:- true:当前线程为守护线程(Daemon Thread)<br />- false:当前线程为用户线程(User Thread)<br />3. 守护线程不会影响 Java 虚拟机的正常停止,一般用于执行一些重要性不是很高的任务(负责关键任务的线程不适合设置为守护线程),比如监视其他线程的运行情况;3. **用户线程会阻止 Java 虚拟机的正常停止**,即一个Java虚拟机只有在其所有用户线程都运行结束(即 Thread.run() 调用未结束)的情况下才能正常停止;3. Java虚拟机被强制停止时比如 Linux 的 kill 命令,用户线程也无法阻止 Java 虚拟机的停止;4、线程优先级 Priority:类型为int,本质是一个给线程调度器的提示,用于表示希望那个线程能够优先得到运行。Java定义了1到10个优先级,默认值为5,表示普通优先级```java// 线程优先级常量public final static int MIN_PRIORITY = 1;public final static int NORM_PRIORITY = 5;public final static int MAX_PRIORITY = 10;// 设置线程优先级Thread.currentThread().setPriority(Thread.MAX_PRIORITY);// 获取当前线程的优先级int priority = Thread.currentThread().getPriority();
注意事项:
- 不正确的优先级设置可能会产生线程饥饿
 - 优先级只是给线程调度器的提示信息,以便线程调度器决定优先调度哪个程序运行,并不能保证线程按照优先级给定的顺序运行
 
5、线程的层次关系(默认):
- 子线程是否是守护线程取决于父线程;
 - 子线程的优先级为该线程的父线程优先级;
 
Thread 的常用方法
静态方法:
1、static Thread currentThread():返回当前线程,即当前代码的执行线程(对象)
Java 中任何一段代码总是执行在某个线程之中,执行当前代码的线程就被称为当前线程。Thread.currentThread() 可以返回当前线程,由于同一段代码可能被不同的线程执行,因此当前线程是相对的,即Thread.currentThread 的返回值在代码实际运行的时候可能对应着不同的线程(对象)。
public class SubThread extends Thread {public SubThread() {// 输出 Thread.currentThread()System.out.println("Thread.currentThread():" +Thread.currentThread().getName());// 输出 this.getName()System.out.println("This.getName():"+this.getName());}@Overridepublic void run() {// 输出 Thread.currentThread()System.out.println("Thread.currentThread():" +Thread.currentThread().getName());// 输出 this.getName()System.out.println("This.getName():" + this.getName());}public static void main(String[] args) {// 构造方法输出:// Thread.currentThread():main// This.getName():Thread-0final SubThread thread = new SubThread();thread.setName("测试线程");// 运行时输出:// Thread.currentThread():测试线程// This.getName():测试线程thread.start();// 使用Thread(Runnable) 实现线程Thread runnable = new Thread(thread);// 运行时输出:即this指针的是赋值后的线程对象// Thread.currentThread():Thread-1// This.getName():测试线程runnable.start();}}
2、static void sleep(long millis):是当前线程休眠(暂停运行)指定的时间
3、static void yield():使当前线程主动放弃其处理器的占用,这可能导致当前线程被暂停
注意:这个方法是不可靠的,该方法被调用时当前线程可能仍然继续运行(视系统的运行情况而定)
实例方法:
- void run():用于实现线程的任务处理逻辑,该方法一般由Java虚拟机直接调用,而不是应用程序调用
 - void start():启动相应线程,该方法的返回并不代表相应的线程已经被启动,一个 Thread 实例的 start 方法只能被调用一次,多次调用会抛出异常
 - void join():等待相应线程运行结束,即如果A线程调用B线程的join()方法,那么A线程的运行会被暂停,直到B运行结束
 - isAlive():判断线程是否已经结束
执行结果:public static void main(String[] args) {final Thread threadA = new Thread(() -> {System.out.println("线程A开始执行");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程A执行完成");});final Thread threadB = new Thread(() -> {System.out.println("线程B正在执行,然后调用ThreadA的join()方法");try {threadA.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程B再次执行");});threadB.start();threadA.start();}

 
被废弃的方法:
- stop:中止线程运行
 - suspend:暂停线程运行
 - resume:与suspend连用,使被 suspend 方法暂停的线程继续运行
 - destory:未实现的方法
 
