Java 多线程学习笔记
概念
进程
- 正在运行的程序,是系统进行资源分配和调用的独立单位
- 每一个进程都有它自己的内存空间和系统资源,一个进程包括由操作系统分配的内存空间,包含一个或多个线程
- 一个进程一直运行,直到所有的非守护线程都结束运行后才能结束
线程
- 线程是进程中的单个顺序控制流,是一条执行路径
- 一个进程如果只有一条执行路径,则称为「单线程程序」
- 一个进程如果有多条执行路径,则称为「多线程程序」
- 一个线程不能独立的存在,它必须是进程的一部分
- 线程是 CPU 调度的最小单位
Java 中对于线程的描述是 Thread
,其中,封装了线程的相关信息,最重要的有需要执行的任务的信息;
线程的交互
互斥和同步:
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。- 互斥是当一个线程正在运行时,其他的线程只能等待,当完成后就可以运行了。
- 同步是两个或多个线程同时进行运行。
线程的状态
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止
- 创建(new)状态: 准备好了一个多线程的对象
- 就绪(runnable)状态: 调用了
start()
方法, 等待 CPU 进行调度 - 运行(running)状态: 执行
run()
方法 - 阻塞(blocked)状态: 暂时停止执行, 可能将资源交给其它线程使用
- 终止(dead)状态: 线程销毁
多线程的意义
- 多进程的意义? 提高CPU的使用率
- 多线程的意义? 提高应用程序的使用率
Java 程序运行原理
Java 命令会启动 Java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个「主线程」,然后主线程去调用某个类的 main
方法。
所以,main
方法运行在主线程中。未采用多线程时,程序都是单线程的。
扩展阅读
线程实现方式
线程创建方式——继承 Thread 类
- 子类继承 Thread 类,子类中覆盖父类方法的
run
方法,将线程运行的代码放在run
方法中; - 创建子类的实例,线程被创建;
- 调用子类的实例的
start
方法,开启线程;
public class Test {
public static void main(String[] args) {
System.out.println("主线程ID:"+Thread.currentThread().getId());
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.run();
}
}
class MyThread extends Thread{
private String name;
public MyThread(String name){
this.name = name;
}
@Override
public void run() {
System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
}
}
执行结果:
主线程ID:1
name:thread2 子线程ID:1
name:thread1 子线程ID:10
同个线程的 start 方法重复调用的话,会出现 java.lang.IllegalThreadStateException
异常
线程创建方式——实现 Runnable 接口
- 子类实现
Runnable
接口,覆盖接口中的run
方法; - 通过
Thread
类创建线程,并将实现了Runnable
接口的子类对象作为Thread
类的参数; - 通过 Thread 类的实例对象调用
start
方法,开启线程;
public class Test {
public static void main(String[] args) {
System.out.println("主线程ID:"+Thread.currentThread().getId());
MyRunnable runnable1 = new MyRunnable("thread1");
Thread thread1 = new Thread(runnable1);
thread1.start();
MyRunnable runnable2 = new MyRunnable("thread2");
Thread thread2 = new Thread(runnable2);
thread2.run();
}
}
class MyRunnable implements Runnable{
private String name;
public MyRunnable(String name){
this.name = name;
}
@Override
public void run() {
System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
}
}
执行结果:
主线程ID:1
name:thread2 子线程ID:1
name:thread1 子线程ID:10
不管是扩展 Thread
类还是实现 Runnable
接口来实现多线程,最终还是通过 Thread
的对象的API 来控制线程的,熟悉 Thread
类的 API 是进行多线程编程的基础。
在 Java 中,这 2 种方式都可以用来创建线程去执行子任务,具体选择哪一种方式要看自己的需求。直接继承 Thread
类的话,可能比实现 Runnable
接口看起来更加简洁,但是由于 Java 只允许单继承,所以如果自定义类需要继承其他类,则只能选择实现 Runnable
接口。
线程创建方式——实现 Callable 接口
还有一种是实现 Callable 接口,并与 Future、线程池结合使用
Thread 常用方法
getName()
获取当前线程的名称Thread.currenthrad().getName
;
继承Thread类,getName()获取当前线程名称。
实现Runnable接口,Thread.currentThread().getName();获取当前线程名称。
- Thread.sleep(1000) 休眠 1 秒;
FAQ
启动一个线程是 run() 还是 start()?它们的区别?
start()
启动一个线程;
run
方法封装了被线程执行的代码,直接调用仅仅是普通方法的调用;start
启动线程,并有 JVM 自动调用run
方法;
Thread和Runnable的区别
如果一个类继承 Thread
,则不适合资源共享。但是如果实现了 Runable
接口的话,则很容易的实现资源共享。
总结,实现 Runnable
接口比继承 Thread
类所具有的优势:
- 适合多个相同的程序代码的线程去处理同一个资源
- 可以避免 Java 中的单继承的限制
- 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
- 线程池只能放入实现
Runable
或callable
类线程,不能直接放入继承Thread
的类
提醒一下大家:main
方法其实也是一个线程。在 Java 中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到 CPU 的资源。
在 Java 中,每次程序运行至少启动 2 个线程。一个是 main
线程,一个是垃圾收集线程。因为每当使用 Java 命令执行一个类的时候,实际上都会启动一个 JVM,每一个 JVM 实际就是在操作系统中启动了一个进程。
参考
- noteless-1-5 java 多线程 概念 进程 线程区别联系
- 嘟嘟MD-importnewJava多线程干货系列(1):Java多线程基础
- Evankaka-CSDN-Java多线程学习(吐血超详细总结)
- 简书-SmartSean-Java多线程基础学习
- 延陵缥缈-CSDN-java多线程编程实例
- IBM-Java 多线程与并发编程专题
- 慕课网-深入浅出Java多线程
