多线程相关概念
线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。(应用软件中相互独立,可以同时运行的功能)
多线程:有了多线程,可以让程序同时做多件事情
多线程作用:提高效率
多现场运用场景:只要想让程序同时做多件事情,就需要多线程
并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行
多线程的实现方式
①继承Thread类的方式进行实现
②实现Runnable接口的方式进行实现
③利用Callable接口和Future接口方式实现
多线程的第一种启动方式:
1.自己定义一个类继承Thread
2.重写run方法
3.创建子类的对象,并启动线程
多线程的第二种启动方式:
1.自己定义一个类实现Runnable接口
2.重写里面的run方法
3.创建自己类的对象(MyRun)
4.创建一个Thread类的对象,并开启线程
多线程的第三种启动方式:
特点:可以获取到多线程运行结果
1.创建一个类MyCallable实现Callable接口
2.重写call(是有返回值的表示多线程运行结果)
3.创建MyCallable的对象(表示多线程要执行的任务)
4.创建FutureTask的对象(作用管理多线程运行的结果)
5.创建Thread类的对象(表示线程),并启动
三种实现方式对比:
优点 | 缺点 | |
继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 可扩展性较差,不能再继承其他的类 |
实现Runnable接口 | 扩展性强,实现该接口的同时还可以继承其他类 | 编程相对复杂,不能使用Thread类中的方法 |
实现Callable接口 |
Thread中常见的成员方法:
sleep(休眠时间毫秒):
线程优先级:
守护线程(备胎线程):
应用场景:比如在QQ中给对方发文件,如果此时把QQ关了,那么文件自然也会停止发送
礼让线程:
插入线程:
线程的生命周期:
如图(来自黑马程序员):
同步代码块:
假如我要写三个售票口进行买票(每个线程代表一个窗口),代码如下:
输出:
我们可以发现输出不是从0开始的,并且存在倒序和跳序,这是因为不同的线程间会存在抢夺关系,比如线程0执行到了ticket++,但在它要执行下一句打印前,线程1、线程2也执行了ticket++,这样就会使我们的ticket增加到2,并且线程1首先执行了打印,所以就会出现无序的状态,那么如何去避免这种状况呢?这时就需要同步代码块的知识了,它相当于对程序上了一把锁,当一个线程进入之后,就会把程序锁住,只有当里边的线程执行完后才会放下一个线程进去,这样就会实现有序输出。
同步代码块格式:
//锁对象是任意的,但要保证唯一,一般用当前类的字节码对象
synchronized (锁){
操作共享代码块
}
同步方法:
提一嘴:StringBuilder
实例不能安全使用多线程。 如果需要同步,那么建议使用StringBuffer
Lock锁:
void lock():获得锁
void unlock():释放锁
Lock时接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock()的实例
死锁:
这是一种错误现象,诸位尽量不要写产生死锁的代码,死锁一般会由两个嵌套的锁产生,所以尽量不要写类似下面图片中的代码
生产者消费者(等待唤醒机制):
生产者:产生数据
消费者:消费数据
常用方法:
方法名称 | 说明 |
void wait() | 当前线程等待,直到被其他线程唤醒 |
void notify() | 随即唤醒单个线程 |
void notifyAll() | 唤醒所有线程 |