线程
一.简介
1.进程
程序:程序就是为了完成特定任务,用某种语言编写的一组指令的集合,就是一段静态的代码
进程:在操作系统中独立运行的程序,每运行一个应用程序就对应一个进程 process
就是一个正在运行的exe文件,进程是动态的,有启动到运行 到结束的一个过程
2.线程
线程: 是进程内部的一个执行单元,用来执行应用程序中的一个功能
多线程:在一个应用程序中可以同时执行多个功能,每一个功能就对应一个线程 迅雷中 同时执行多个下载任务,就是多个线程在运行
特性:
- 一个进程中可以有多个线程
- 一个进程中的多个线程共享进程中的资源
3.CPU时间片
对于单核CPU,某个时间点只能处理一件事情
CPU为 各个程序分配时间,称为时间片,该进程运行的时间(时间很短)
- 从表面看每个程序同时运行的,实际上CPU在同一个时间点只能执行一个程序
- 只是CPU在很短的时间内,在不同程序之间进行切换,轮流执行每个程序,执行的速度很快,感觉上在同时执行
二.线程的创建
实际上,java程序在运行是至少有两个线程,主线程,垃圾回收线程(gc)
- 主线程: JVM启动时会创建一个主线程,用来执行main()方法中的代码
- 垃圾回收线程: 低级别的线程,用来回收垃圾变量
- 如果需要实现多个线程,可以创建新的线程(自定义线程)
两种方式:
- 继承Thread类
- 实现Runable接口
1.继承Thread类
- 定义一个类,继承Thread类,Thread类是线程的父类,提供了一些操作线程的方法
- 重写父类中run()方法
- 创建该类的实例,创建线程对象
- 启动线程,线程对象调用start()方法。不能调用run()方法。start()被调用不是指线程一定能运行,还的看是否能够抢到CPU
2.实现Runable接口
- 定义一个类,继承Thread类,Thread类是线程的父类,提供了一些操作线程的方法
- 重写父类中run()方法
- 创建该类的实例,创建线程对象
- 创建Thread类,将自己的线程类对象传入
- 启动线程,线程对象调用start()方法。不能调用run()方法
3.两种方式的对比
继承Thread
- java单一继承,无法继承其他类
实现Runable接口
- 可以继承其他类,避免了单一继承的局限性
- 适合多个相同程序代码的线程去处理同一个资源(共享一个资源)
4.线程的生命周期
1.线程的多种状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1tHkrmTY-1610940016669)(img/线程的生命周期.png)]
2.方法
方法 | 含义 |
---|---|
start() | 启动线程,就入就绪状态 |
sleep() | 休眠线程,线程从运行状态 进入 阻塞状态 静态方法 |
join() | 暂停线程的执行,等待另一个线程加入进来,执行完成。从运行状态进入阻塞状态 |
yield() | 暂停线程(让出CPU资源), 线程从运行状态进入就绪状态,只是一瞬间的事,其他线程不一定能够执行。 静态方法 |
interrupt | 中断线程的休眠状态,如果一个线程的休眠被中断,会报错 |
三.线程的安全性问题
1.线程安全问题
多个线程同时访问共享数据可能会出现问题,称为线程安全问题 ---- 异步线程
当多个线程同时访问共享数据,由于cpu切换,导致一个线程只执行了一部分代码,没有执行完成,此时另一个线程参与进来,导致共享数据发生异常
2.同步线程 和异常线程
- 同步线程:在多个线程同时执行时,一个线程要等待上一个线程执行完成后,才开始执行。 ----- 排队
- 异步线程:在对个线程同时执行时,不用等待上面的线程是否结束,一起执行。
3.解决安全问题
线程的同步机制(异步线程— 同步线程)
synchronized +锁
-
同步方法 ---- synchronized 修饰方法
-
同步代码块— synchronized 修饰代码块
synchronized ( 对象锁-对象名){
//同步的代码
}
锁:也称为对象锁,每个对象都自带一个锁(标识),不同对象的锁不一样
执行过程:
- 当线程执行同步方法或者同步代码块时,必须获取特定对象的锁,才能执行
- 一旦一个对象的锁被获取了,则该对象不再拥有锁,直到线程执行完代码,才会释放锁
- 如果线程无法获取特定对象的锁,则线程会进入对象的锁池中等待,直到锁被归还,此时需要锁的线程可以去竞争锁。
四.线程的通信
1.锁池和等待池
每个对象都有锁池和等待池
锁池:
- 当线程无法获取对象锁,此时进入锁池中
- 如果对象锁被归还,锁池中的多个线程竞争锁
等待池:
- 当线程获取锁后,可以调用wait()方法,放弃锁,进入等待池
- 当其他线程调用对象的notify notifyAll方法时,等待池中的线程被唤醒,进入锁池
- 在锁池中可以继续竞争锁
2.方法
方法 | 含义 |
---|---|
wait() | 放弃对象锁 |
notify() | 随机唤醒一个等待池中的线程 |
notifyAll() | 唤醒等待池中的所有线程 |
注意:
- 这三个方法 只能在synchronized块中使用,只有获取了锁的线程才能调用
- 等待和唤醒必须使用同一个对象
3.线程通信的案列
模拟生产者和消费者的问题
生产者线程:
- 生产商品,放入仓库
- 当仓库满了,停止生产
消费者线程:
- 消费商品,从仓库中取出
- 当仓库为空时,停止消费
五.线程单例
1.简介
同一个线程中获取的是同一个对象,不同线程中获取不同对象
java中提供了一个ThreadLocal类,直接为实现了线程单例
用来管理对象,在同一个线程中多次获取对象,都是同一个对象