JAVA 多线程基础
多线程基础内容
1.1 线程的五种状态
- new 线程创建
- runnable 调用start进入可执行状态
- running 此时执行run()方法中的业务代码
- blocked 中断状态 sleep wait yield 等
- terminated 线程的最终状态
tips:
- Thread 中的run和start -> 模板设计模式
- runnable -> 策略模式
1.2 线程的实现方式
- 继承Thread
- 实现Runnable
- 实现Callable
/**
* 线程的多种实现方式
*
* @author yang yang
* @create 2018/10/20
*/
@Slf4j
public class TestThread {
public static void main(String[] args) {
TestThread thread = new TestThread();
thread.thread().start();
thread.runnable().start();
thread.callable();
}
/**
* <p>
* 线程的继承实现
* </P>
*
* @return
*/
public Thread thread() {
ThreadExtendImpl thread = new ThreadExtendImpl("thread");
return thread;
}
/**
* <p>
* 线程的runnable实现方式
* </P>
*
* @return
*/
public Thread runnable() {
ThreadRunnableImpl runnable = new ThreadRunnableImpl();
runnable.setName("runnable");
Thread thread = new Thread(runnable);
return thread;
}
/**
* <p>
* 线程的callable实现方式
* </p>
*/
public void callable() {
ThreadCallableImpl<User> callable = new ThreadCallableImpl<>();
callable.setName("callable");
FutureTask<User> futureTask = new FutureTask<User>(callable);
new Thread(futureTask).start();
try {
User user = futureTask.get();
log.info(user.toString());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
/**
* <p>
* 继承方式实现
* </p>
*/
@Slf4j
class ThreadExtendImpl extends Thread {
public ThreadExtendImpl(String name) {
this.setName(name);
}
@Override
public void run() {
System.out.println(this.getName() + " is running !");
}
}
/**
* <p>
* runnable方式实现
* </P>
*/
@Slf4j
class ThreadRunnableImpl implements Runnable {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name + " is running !");
}
}
/**
* <p>
* callable方式实现
* </P>
*
* @param <AnyType>
*/
@Slf4j
class ThreadCallableImpl<AnyType> implements Callable<AnyType> {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public AnyType call() throws Exception {
log.info(this.name + " is running !");
User build = User.builder().name("yangy").gender(1).desp("测试").phone("17700000000").build();
return (AnyType) build;
}
}
注意
- 重复启动线程会抛出异常
- 线程启动后会被加入到线程组中
- Thread被构造后的NEW状态,threadStatus 内部属性值为0
- 线程一旦进入terminated 状态,即一个线程的生命周期结束时,不允许再调用start()方法
tips:
- 类中的final方法无法被重写
- 一个线程的创建的肯定是有另一个线程完成的
- 被创建线程的父线程是创建它的线程
- 构造线程时如果没有显示的指定一个ThreadGroup,那么子线程会被加入父线程所在的线程组
1.3 线程与线程组
- 线程的创建肯定是由另一个线程完成的
- 被创建线程的父线程是创建它的线程
- main线程所在的ThreadGroup成为Main
- 构造线程时如果没有显式声明所在的线程组,那么它会和父线程同属一个线程组
tips:
- 守护线程(后台线程)
- 当前线程的优先级不能高于当前所在线程组的优先级
1.4 线程的sleep方法和yield方法和Join
- sleep会导致当前线程停顿指定时间,没有CPU时间片的消耗
- yield只是对CPU调度器的一个提示,类似于GC的提示,执行不执行取决于CPU
- sleep会使线程短暂BLOCK,在给定时间内释放CPU资源
- 一个线程sleep另一个线程调用interrupt会捕捉到中断信号,yield不会
- B线程joinA线程,会使线程B进入等待状态,知道线程A结束生命周期,或者到达给定的时间
tips:
- sleep方法不会释放锁资源
1.5 jvm内存结构
- 程序计数器
- Java虚拟机栈
- 线程私有
- 生命周期与线程相同
- 在JVM运行时创建
- 本地方法栈
- 线程私有
- 堆内存(GC堆)
- 所有线程共享
- 运行期间创建
- 新生代老年代(EDEN|FROM SURVIVOR|TO SURVIVOR)
- 方法区(非堆)
- java8元空间
1.6 线程interrupt
- interrupt 可以打断线程的阻塞状态
- interrupted 静态方法 会擦除线程的interrupt标识
- isInterrupted 不会擦除线程的interrupt标识
tips:
- interrupted和isInterrupted调用的是同一个方法 传递的参数值不一样
1.7 线程的关闭
- 正常关闭
-
线程结束生命周期正常结束
-
捕获中断信号关闭线程
-
使用volatile开关控制
-
- 异常退出
- 线程的执行单元中,不允许抛出checked异常的
- 进程假死
- 线程阻塞
- 线程出现假死
2.1 线程安全与数据同步
synchronized 关键字使用方法
- 同步方法
- 同步代码块
tips:
synchronized关键字提供了一种互斥机制,即同一时刻只能有一个线程访问同步资源
使用误区:
- 锁对象不能为空
- 作用域太大 作用域越大,效率越低,甚至会丧失并发的优势
- 不同的对象企图锁相同的方法
- 多个锁的交叉导致死锁
2.2 程序死锁原因及如何诊断
- 交叉锁可导致出现死锁
- 内存不足
- 一问一答式的数据交换
- 数据库锁
- 文件锁
- 死循环引起的死锁
2.3 wait和notify
- wait是可中断方法
- 线程执行了某个对象的wait方法后,会加入预支对应的wait set中,每一个对象的monitor都有一个与之关联的wait set
- 必须在同步方法中使用wait和notify方法,因为执行wait和notify的前提小件是必须持有同步方法的monitor的所有权
tips:
- wait和sleep 都可以使线程进入阻塞状态,都可被中断
- wait 是Object的方法,sleep是线程特有的方法
- wait方法的执行必须在同步方法中
- wait方法会释放锁,sleep不会
- sleep短暂时间休眠后会主动退出阻塞
2.4 线程组
2.5 Hook线程以及捕获线程执行异常
- HOOK在JVM进程退出时,会启动执行
- 可以注入多个Hook线程
tips:
- Hook线程只有收到退出信号是才会被执行, kill -9 进程会立即退出,不会有退出信号
- Hook线程中可以执行资源释放的工作
- Hook线程中尽量避免执行一些耗时非常长的操作,会导致程序长时间未退出