1. 线程和并发编程含义
1.0 什么是线程
我们运行一个程序,要运行一个main方法,执行main方法相当于其实是启动一个jvm进程,jvm进程里是有很多线程的,首先第一个线程就是你能看到的main线程,只要你的线程执行完了一段代码后,其实jvm进程就退出了
一个jvm进程里,除了人家默认给你开启的main线程,你还可以在mian线程里面开启别的线程,比如通过Thread类开启别的线程,别的线程是跟main线程同时在运行的
多线程并发运行的时候,是没有先后顺序的,本质是CPU在执行各个线程的代码的,一个CPU会有一个时间片算法,他一会执行main线程,一会执行Thread线程,只不过CPU执行每个线程的时间特别短,可能执行一次就几毫秒,或者是几微秒的样子,其实是感觉不出来的,看起来是多线程在并发运行的
1.1 什么是并发编程
一句话:用多线程来编程,实现比较复杂的系统的功能,让多个线程同时运行,干各种事情,最终完成一套复杂系统来干的事
2. 线程的创建和启动
new Thread() {
public void run() {
}
}.start();
public class MyThread extends Thread {
public void run() {
}
}
new MyThread().start();
new Thread(new Runnable() {
public void run() {
}
}).start();
public class MyRunnable implements Runnable {
public void run() {
}
}
new Thread(new MyRunnable()).start();
用Thread,Runnable来封装线程逻辑都可以,我一般是用Thread来实现一个线程
2.0 工作线程
我们启动了一个jvm进程,main线程,RegisterClientWorker线程
main线程负责启动了RegisterClientWorker线程,其实干完这些事情以后,main线程就结束了,结束了以后但是jvm进程不会退出?因为有一个工作线程,就是RegisterClientWorker线程一直在运行,所以jvm进程是不会退出的,会一直存在
3. daemon线程
3.0 daemon解释
在java里有一个daemon线程的概念,这个意思就是说,如果jvm的工作线程都停止了,比如main线程之类的线程都执行完了,那么daemon线程会跟着jvm进程一起退出,不会像工作线程一样阻止jvm进程退出
比如常见的,假如说微服务注册中心负责接收请求的核心工作线程不知道为啥就停止了,那么说明这个微服务注册中心必须停止,如果你的那个监控微服务存活的状态的线程一直在那运行,卡着,会导致微服务注册中心没法退出的,因为jvm没法结束
所以针对这种情况,一般会将后台运行的线程设置为daemon线程,如果jvm里只剩下了daemon线程,那么就会进程退出,所有的daemon线程一起销毁了,不会阻止jvm进程退出。所以我们需要将微服务存活状态监控的线程,设置为daemon线程
3.1 将线程设置为daemon
daemon.setDaemon(true);
只要设置了这个标志位,就代表了这个线程是一个daemon线程,也可以叫它后台线程
非daemon线程,我们一般叫它工作线程,如果工作线程都退出了,那么daemon线程是不会阻止jvm进程退出的,daemon线程会跟着jvm线程一起退出
4. 线程优先级
daemon.setPriority(8);
这个设置1-10,
理论上来说,你的线程优先级越高的话,你的线程会更有可能被cpu来执行,而且ThreadGroup也可以指定优先级,线程优先级不能大于ThreadGroup的优先级,默认的线程的优先级都是5
5. ThreadGroup
大体来说,ThreadGroup就是线程组,其实意思就是你可以把一堆线程加入一个线程组里,这个好处就是,你可以将这些线程作为一个整体,统一的管理和设置
实际上,java中,每个线程都有父线程的概念,就是在那个线程里创建这个线程,那么它的父线程就是那个线程,比如,java都是通过main线程来启动的,那么有一个主要的线程就是main线程,在main线程里启动的线程,那么父线程就是mian线程
其实每个线程都必然属于一个线程组,默认情况下,你要是创建一个线程没有指定一个线程组,那么就会属于父线程的线程组,main线程的线程组就是main ThreadGroup
6. 线程sleep
Thread.sleep();这里里面传的是一个毫秒数
在jdk1.5以后,引入了TImeUnit这个类,很方便
TimeUnit.HOURS.sleep(1)休眠一个小时。。。
其实在线程sleep这里,还是使用Thread.sleep(),因为可以通过毫秒数来动态传入一个配置的值
7. 线程yield
线程礼让,几乎不用
8. 线程join
比如说mian线程中,开启了一个其他线程,这个时候,只要你一旦开启了其他线程后,那么main线程就和其它线程一起并发执行,就是说一会执行mian线程,一会执行其他线程
main线程里面开启了一个线程,main线程如果对那个线程调用了join的方法,那么就会导致main线程就会阻塞住,它会等待其他线程的代码逻辑执行结束,那个线程执行完毕,然后main线程才会往下走。
registerWorker.join();
9. 线程interrupt
线程打断
this.heartbeatWorker.interrupt();
并不是说,直接对一个线程interrupt一下某个线程,直接就不让他运行了,而是可以打断他的休眠操作
public static void main(String[] args) throws Exception {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.setShouldRun(false);
thread.interrupt();
}
private static class MyThread extends Thread{
private Boolean shouldRun = true;
@Override
public void run() {
while (shouldRun){
try {
System.out.println("线程1在执行工作");
Thread.sleep(30 * 1000);
} catch (Exception e){
e.printStackTrace();
}
}
}
public void setShouldRun(Boolean shouldRun){
this.shouldRun = shouldRun;
}
}
这里在运行了一秒后打断睡眠然后线程退出
9.0 interrupt的使用场景
在一个分布式系统里面,一般会有一些核心的工作线程,现在如果这个系统要关闭,一般会设计一个shutdown的方法,在这个方法里面,会设置各个工作线程是否需要运行的一个标志位为false,然后对各个线程都执行interrupt,
因为各个工作线程都可能在不断的while循环运行,但是每次执行完一次以后,都会进入休眠的状态,如果系统要尽快的停止,那么就应该用interrupt打断各个工作线程的休眠,让他们判断是否运行的标志位为false,就立刻退出,只要所有线程结束,那么jvm进程就会自动退出