Java基础 | Multithread
1.多线程概述
程序需要多个任务时候
程序需要实现一些需等待的任务[用户输入][文件读写]等等
需要一些后台运行的程序时
start方法作用,启动当前线程,调用当前线程的run()方法
优点
提高应用程序响应,增强用户体验,提高电脑CPU的利用率
改善程序结构,把复杂的进程改为多个线程,独立运行便于理解和修改
2.程序|进程|线程
程序
是用某种语言编写的一组指令集合
即一段静态的代码,静态对象
进程
程序的一次执行过程/一个正在运行的程序即是一个动态的过程
有它自身产生、存在和消亡的过程[生命周期]
电脑上的任务管理器中的一个程序就代表一个进程
线程
进程再进行细化就是线程,即一个程序内一条执行路径
若一个进程在同一时间并行执行多个线程[多线程]
例:360卫士中的每一个清理选项,就是一个线程
多个清理选项同时进行,就是多线程了
3.并发并行
并发concurrency
一个CPU同时处理多个任务,每个任务相当一个线程
给每一个线程分配一点时间,时间一到到切换另外一个线程
并行parallelism
多个CPU同时处理多个任务
4.线程声明周期
5.创建线程方式
5.1继承Thread类
1 |
|
5.2实现Runnable接口
1 |
|
5.3实现Callable接口
1 |
|
5.4使用Excutor线程池
1 |
|
6.线程安全问题
同时满足两个条件时
1.多个线程执行的不确定性引起执行结果的不稳定性
2.多个线程在操作共享数据,会造成系统操作的不完整性,会破坏数据
3.解决方案使用synchronized关键词或lock接口
6.1同步代码块
1 |
|
- 共享数据:多个线程都要操作的变量
- 同步监视器:就是锁,任意对象都可充当锁
- 多个线程必须使用同一把锁
- 实现Runnable接口
Sychronized(this)或Sychronized(new的对象名) - 继承Thread类
Sychronized(当前类名.class)或Sychronized(new的对象名)
Sychronized(this)慎用充当同步监视器
好处
解决了线程安全的问题
弊端
操作同步代码时,只能一个线程执行,其余等待
就相当于单线程了,效率变低了(jdk1.5以后lock接口解决了此问题)
6.2同步方法
前提条件
共享数据:多个线程都要操作的变量
同步监视器:就是锁,任意对象都可充当锁
如果操作共享数据的代码刚好在一个方法里面,把此方法声明为同步的即可
- 同步方法仍然涉及到同步监视器,不需要我们声明
- 非静态同步方法默认为this
- 静态同步方法默认为当前类本事
6.3Lock接口
Jdk1.5开始java提供了锁对象来同步问题
同步锁使用Lock对象充当
Lock锁本身是一个接口,无法进行实例化
通常使用reentrantlock进行实例化
6.4Synchronized和Lock的区别
Synchronized
是关键字,在jvm层面,无法判断是否获取锁的状态
会自动释放锁
a 线程执行完同步代码会释放锁
b 线程执行过程中发生异常会释放锁
线程1和线程2
如果当前线程1获得锁,线程2等待
如果线程1阻塞
线程2则会一直等待下去
锁可重入、不可中断、非公平,适合代码少量的同步问题
Lock
是接口由ReentrantLock类实现,可以判断是否获取到锁
Lock需在finally中手工释放锁(unlock方法)
否则容易造成线程死锁
线程1和线程2
Lock锁就不一定会等待下去
如果尝试获取不到锁
线程可以不用一直等待就结束了
锁可重入、可中断、可公平,适合大量同步的代码的同步问题
7.线程的通讯
即线程在运行中遇到的一些状态,需要通过一些方法去解决
notify()
唤醒正在等待的单个线程(优先级高的优先)
notifyAll()
唤醒正在等待的所有线程
wait()
使得当前线程等待,直到另一个线程调用该对象notify或者notifyAll方法
sleep与wait的区别
- sleep方法必须传入参数,参数是时间,时间到了自动醒来
- 可以在任何需要场景下去调用
- sleep定义在thread类,不释放锁
- wait方法可以传入参数,也可以不传入参数
- 只能在同步方法或者同步代码块中
- wait定义在Object类,释放锁
8.线程的调度
Sleep
1 |
|
Yield
1 |
|
Daemon
1 |
|
Join
1 |
|
Priority
1 |
|
9.四种方式对比
Thread类
- Thread类创建线程不适合资源共享
- Thread类本事自己也实现了Runnable接口
Runnable接口
- 避免了Java中单继承的局限性
- 适合多条线程去处理同个资源
- 增强程序的健壮性,代码可被多条线程共享
- 代码和数据之间彼此独立
- 线程池中只能放入runnable或callable类线程
Callable接口(JDK1.5)
- 可以使用泛型
- 可以使用带泛型的返回值
- 可以抛出异常
- 具体实现需要借助FutureTask类
Future接口
- 可以对具体Runnable、Callable任务的执行结果
- 进行取消、查询是否、获取结果等等
- Futuretask是Future接口的唯一实现类
- FutureTask同时实现Runnable和future接口
- 可作为runnable被线程执行
- 也可作为future来获取callable的返回值使用get()方法
Excutor线程池(JDK1.5)
- 开发中常用,多条线程存放在Excutor中
- 使用时候通过方法调用,不用时候释放
- 提高响应速度,降低资源消耗,便于线程管理