多线程
1. 多线程的优缺点
优点
- 提升资源利用率
- 提高用户体验
缺点
- 降低了其他线程的执行概率
- 用户会感受到软件的卡顿问题
- 增加了系统资源的压力
- 多线程情况下的资源共享问题,线程冲突,线程安全问题
2. 创建自定义线程类的两种方式
class Thread类
Java中的一个线程类
Thread类是Runnable接口的实现类,同时提供了很多线程的操作使用的方法
interface Runnable接口
这里规定了what will be run?
里面只有一个方法 run方法
方式一:
自定义线程类,继承Thread类,重写run方法
创建自定义线程类对象,直接调用start方法,开启线程
方式二:
自定义线程类,遵从Runnable接口
使用自定义遵从接口Runnable实现类对象,作为Thread构造方法参数
借助Thread类对象和start方法,开启线程
【推荐】
以上两种方式,推荐使用方式二,遵从Runnable接口来 完成自定义线程,不影响正常的继承逻辑,并且可以使用匿名 内部类来完成线程代码块的书写
3. 自定义线程执行流程简述
4. Thread类需要了解的方法
-
构造方法 Constructor
Thread();
分配一个新的线程对象,无目标,无指定名字Thread(Runnable target);
创建一个新的线程对象,并且在创建线程对象的过程中,使用Runnable接口的实现类 对象作为执行的线程代码块目标Thread(String name);
创建一个新的线程,无指定目标,但是指定当前线程的名字是什么Thread(Runnable target, String name);
创建一个线程的线程对象,使用Runnable接口实现 类对象,作为执行目标,并且指定 name作为线程名 -
成员方法:
void setName(String name);
String getName();
以上两个是name属性setter和getter方法void setPriority(int Priority);
设置线程的优先级,非一定执行要求,只是增加执行的概率
优先级数值范围 [1 - 10] 10最高 1最低 5默认int getPriority();
获取线程优先级 void start(); 启动线程对象public static void sleep(int ms);
当前方法是静态方法,通过Thread类调用,要求是当前所在线程代码块对应的线程, 进行休眠操作,休眠指定的毫秒数public static Thread currentThread();
当前方法是静态方法,通过Thread类调用,获取当 前所处代码块对应的线程对象。
线程安全问题和解决方案
1. 线程安全问题—贡献资源能使用问题
2. 同步代码块
synchronized (/* 锁对象 */) {
}
特征:
- synchronized 小括号里面的对象是锁对象,并且要 求如果是多线程的情况下,锁对象必须是同一个对象。
- synchronized 大括号中的代码块就是需要进行同步的代码,或者说是加锁的代码,大括号里面的内容,有且只允 许一个线程进入。
- 同步代码块越短越好,在保证安全的情况下,提高性能问题:
a. 目前锁对象感觉很随意,存在一定的隐患
b. 代码层级关系很复杂,看着有点麻烦
3. 同步方法
synchronized 作为关键字来修饰方法,修饰的方法就是对应的同步方法。有且只有一个线程进入,到底是谁来完成加锁操作?
- 静态成员方法
锁对象,是当前类对应的字节码文件 .class 类名.class - 非静态成员方法
锁对象就是当前类对象 this
选择同步方法是否使用static修饰问题
-
如果非static修饰,要保证执行的线程对象有且只有一个,因为锁对象当前线程对
-
如果是static修饰,锁对象具有唯一性,多个线程使用的锁是同一个锁
4. Lock锁
- 对象化操作。
Lock lock = new ReentrantLock(); - 方法化操作
开锁:
unlock();
加锁:
lock();
5. 三种加锁总结
- 一锁一线程,一锁多线程问题
使用对应锁操作对应的线程,考虑静态和非静态问题。
同步方法和Lock锁使用
静态是一锁多目标,非静态是一锁一目标 - 涉及到同步问题时,要考虑好锁对象的选择问题
同步代码块,同步方法,Lock对象
守护线程
守护线程,也称之为后台线程。如果当前主线程GG,守护线程也GG。
守护线程一般用于:
- 自动下载
- 操作日志
- 操作监控
方法是通过线程对象
setDeamon(Boolean flag);
true为守护线程
false为缺省属性,正常线程
线程状态
1. 六种线程状态
状态 | 导致状态的发生条件 |
---|---|
NEW(新建) | 线程刚刚被创建,没有启动,没有调用start方法 |
RUNNABLE(可运行) | 线程已经可以在JVM中运行,但是,是否运行不确定,看当前线程是否拥有CPU执行权 |
BLOCKED(锁阻塞) | 当前线程进入一个同步代码需要获取对应的锁对象,但是发现当前的多对象已经被其他线程持有,当前线程会进入一个BLOCKED。如果占有锁对象的线程打开锁对象,当前线程持有锁对象,进入Runnable状态。 |
WAITING(无限等待) | 通过一个wait方法线程进入一个无限等待状态,这里需要另外一个线程进行唤醒操作。进入无限等待的线程是无法自己回到Runnable状态,需要其他线程通过notify或者notifyAll方法进行唤醒操作 |
TIMED_WAITING(计时等待) | 当前线程的确是等待状态,但是会在一定时间之后自动回到Runnable状态,例如 Thread.sleep() 或者是Object类内的wait(int ms) |
TERMINTATED(被终止) | 因为Run方法运行结束正常退出线程,或者说在运行的过程中因为出现异常导致当前的线程GG |
2. TIMED_WAITING(计时等待)
Thread.sleep(int ms);
在对应的线程代码块中,当前线程休眠指定的时间。
Object类内 wait(int ms)
让当前线程进入一个计时等待状态
1. 规定的时间及时完毕,线程回到可运行状态
2. 等待时间内,通过其他线程被notify或者notifyAll唤醒
sleep方法
1.调用之后休眠指定时间
2.sleep方法必须执行在run方法内,才可以休眠线程
3. sleep不会打卡当前线程占用的锁对象
3. BLOCKED锁阻塞
线程中有锁存在,线程需要进入带有锁操作的同步代码。如果锁对象被别人持有,只能在锁外等待
锁阻塞状态的线程是否能够抢到锁对象有很多因素
1. 优先级问题,非决定因素
2. CPU执行概率问题