- 线程简介:程序、进程、线程
- 线程实现:继承Thread类、实现Runnable接口、实现Callable接口
- 线程状态
- 线程同步
- 线程通信问题
- 高级主题
📌概念简介
- 程序:指令和数据的有序集合
- 进程:程序的一次执行过程,他是一个动态的概念,是系统资源分配和调度的基本单位(虚拟存储空间、文件描述符等)。可以把进程理解为在操作系统中运行的程序,比如你的QQ、播放器。
- 线程:是进程的一个执行单元。通常一个进程中包含若干个线程,一个进程中至少一个线程(主线程、GC线程),不然没有存在的意义,线程是CPU调度和执行的单位。每个线程都有各自的线程栈、自己的寄存器环境、自己的线程本地存储。
- 主线程与子线程:JVM启动时会创建一个主线程,该主线程负责执行main方法。如果在A线程中创建了B线程,则称B线程为A线程的子线程,相应的A线程为B线程的父线程。
- 串行、并发(Concurrent)与并行(Parallel):串行是以任务依次执行,并发在任意时刻只能有一条指令执行,但多个进程指令快速轮转执行(因此每个线程拥有各自的线程栈,自己的寄存器环境和本地存储等少量资源),并行是指同一时刻多条指令在多核上执行。
- 在一个进程中,如果开辟了多个线程,线程的运行有调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
- 对同一个资源操作时,会存在资源抢夺的问题,需要加入并发控制
- 线程会带来额外的开销,如CPU调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
📌线程创建
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
Callable的好处
- 可以定义返回值
- 可以抛出异常
package com.pai.class02;import java.util.concurrent.*;//Callable实现public class TestThread5 implements Callable<Boolean> {@Overridepublic Boolean call() throws Exception {System.out.println(Thread.currentThread().getName());return true;}public static void main(String[] args) throws ExecutionException, InterruptedException {TestThread5 t1 = new TestThread5();TestThread5 t2 = new TestThread5();TestThread5 t3 = new TestThread5();ExecutorService executorService = Executors.newFixedThreadPool(3);Future<Boolean> submit1 = executorService.submit(t1);Future<Boolean> submit2 = executorService.submit(t2);Future<Boolean> submit3 = executorService.submit(t3);Boolean rs1 = submit1.get();Boolean rs2 = submit2.get();Boolean rs3 = submit3.get();executorService.shutdown();}}
线程开启
- 启动线程是调用线程的start()方法,实质是请求JVM运行相应的线程,这个线程具体在什么时候运行由线程调度器(Scheduler)决定。
- start调用并不意味着线程执行,只是在告诉JVM线程就绪,什么时候调度由线程调度器决定。
- 如果开启了开启了多个线程,start()的调用顺序并不意味着线程的启动顺序。
- 当然如果是只有在A指令执行完毕后才调用B线程的start()方法,那么A指令的执行一定在B线程的前面
run()和start()的区别


- this和Thread.currentThread()的区别
📌线程状态

- 什么情况下会进入就绪状态?
📌线程方法
📌线程同步
三大线程不安全案例
📌死锁
📌线程通信
生产者-消费者问题
管程法
信号灯法
📌线程池
Thread的实现原理
Thread的实现采用了静态代理模式。
静态代理模式:真实对象和代理对象都要实现同一个接口,代理对象要代理真实角色 ```java package com.pai.class02;
public class TestStaticProxy { public static void main(String[] args) { WeddingCompany weddingCompany = new WeddingCompany(new You()); weddingCompany.happyMerry();
//与Thread的对比Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("静态代理");}});thread.start();}
} class You implements Merry{ @Override public void happyMerry() { System.out.println(“我要结婚”); } } class WeddingCompany implements Merry{ private You you;
public WeddingCompany(You you) {
this.you = you;
}
@Override
public void happyMerry() {
System.out.println("准备请柬");
you.happyMerry();
System.out.println("收红包");
}
} interface Merry{ void happyMerry(); }
总结:Thread是静态代理对象,实现了Runnable接口的类是被代理对象。
<a name="70uHF"></a>
# 补充——Lambda表达式
<a name="LuNuo"></a>
## 为什么使用lambda表达式?
1. 避免匿名内部类定义过多
1. 可以让你的代码看起来简洁
1. 去掉了一堆没有意义的代码,只留下核心逻辑
<a name="keL3Y"></a>
## 函数式接口
任何接口秒如果只包含唯一的抽象方法,那么他就是一个函数式接口,如下:
```java
public interface Runnable{
public abstract void run();
}
实例
package com.pai.class02;
//lambda表达式
public class TestLambda1 {
//3. 静态内部类
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("静态内部类");
}
}
public static void main(String[] args) {
ILike like = null;
like = new Like1();
like.lambda();
like = new Like2();
like.lambda();
//4. 局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("局部内部类");
}
}
like = new Like3();
like.lambda();
//5. 匿名内部类
like = new ILike() {
@Override
public void lambda() {
System.out.println("匿名内部类");
}
};
like.lambda();
//6. lambda表达式
like = ()->{
System.out.println("lambda表达式");
};
like.lambda();
}
}
//1. 定义一个函数式接口
interface ILike{
void lambda();
}
//2. 定义一个类实现该接口
class Like1 implements ILike{
@Override
public void lambda() {
System.out.println("外部实现类");
}
}
补充——守护线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕。
虚拟机不必等待守护线程执行完毕,如后台记录操作日志,监控内存,垃圾回收等待机制
