学习来源于b站 https://www.bilibili.com/video/av82219418
自己仅作 java 多线程的记录,看视频主要还是看书看不下去了…
然后推荐大家看看《JAVA并发编程实践》
Thread 介绍
Thread 生命周期图
- java 应用程序的main函数是一个线程,是被jvm启动时调用,线程的名字叫main
- 实现一个线程,必须创建Thread 实例,override run方法,并且调用start方法
- 在JVM启动后,实际上有多个线程,但是至少有一个非守护线程
- 当你调用一个线程start方法的时候,此时至少有两个线程,一个是调用你的线程,还有一个执行run方法的线程
下面代码观察,Thread 线程用 run
执行和 start
执行的区别
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("start:"+Thread.currentThread().getName()); // 当前线程名
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("run:"+Thread.currentThread().getName()); // main 线程
}
}).run(); // 调用run方法,仅仅是实例对象调用
}
Thread 构造方法介绍
- 创建线程对象Thread,默认有一个线程名,以Thread-开头,以0开始计数,具体实现参考以下源码
public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }
- 如果构造线程对象时,未传入ThreadGroup,Thread 会默认获取父线程的ThreadGroup 作为该线程的ThreadGroup , 此时子线程和父线程在同一个ThreadGroup中,参考以下源码
/* If the security doesn't have a strong opinion of the matter use the parent thread group. */ if (g == null) { g = parent.getThreadGroup(); }
- 构造Thread 的时候传入stacksize 代表着该线程占用stack大小,如果没有指定stacksize 的大小,默认是0,0代表着会忽略该参数,该参数会被JNI函数去使用。需注意:该参数有一些平台有效,有些平台无效。
Daemon 线程
守护线程
Thread thread = new Thread(() -> {
Thread innerThread = new Thread(() -> {
while(true){
System.out.println("do something...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
innerThread.setDaemon(true); // 设置为守护线程
innerThread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
}
作用:当你线程调用其他线程的时候,你的线程结束时,设置为守护线程会随着外面的线程而结束。
join 方法
等待当前线程执行完后,主线程继续执行的意思。
public static void testJoin() throws InterruptedException {
Thread t = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
Thread.sleep(10000);
}
});
t.start();
t.join();
System.out.println("finish...");
}
上述例子,如果不添加t.join();
的话,里面会把输出语句打印出来,但是添加后,等上面的线程执行完毕后,在执行主线程。当我们统计其他线程的时候,就需要用到join。
线程结束
方式1,利用标记的方式
public class ThreadStop extends Thread {
private volatile boolean start = true;
@Override
public void run() {
while(start){
// TODO
}
}
public void shutdown(){
this.start = false;
}
public static void main(String[] args) throws InterruptedException {
ThreadStop threadStop = new ThreadStop();
threadStop.start();
Thread.sleep(1000);
threadStop.shutdown();
}
}
方式2:利用interrupt方法
public class ThreadStop extends Thread {
@Override
public void run() {
while(true){
if(Thread.interrupted()){
break; // return
}
}
}
public static void main(String[] args) throws InterruptedException {
ThreadStop threadStop = new ThreadStop();
threadStop.start();
Thread.sleep(1000);
threadStop.interrupt();
}
}
暴力解决线程
无论标记,还是打断的方式,都需要在某一处判断,但是多数时候,block的状态,我们才想结束;
而jdk 提供的stop()方法已经过期了。
之前守护线程提起过,如果我们主线程被结束的话,守护进程也是被jvm关闭的。
/**
* @Author: shengjm
* @Date: 2020/2/5 15:48
* @Description: 让传入的线程成为守护线程,这样关闭线程的时候,关闭守护线程
*/
public class ThreadCloseForce {
private boolean finished = false;
private Thread executeThread;
public void execute(Runnable task){
executeThread = new Thread(new Runnable() {
@Override
public void run() {
Thread runner = new Thread(task);
runner.setDaemon(true);
runner.start();
try {
runner.join();
finished = true;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
executeThread.start();
}
public void shutdown(long mills){
long currentTime = System.currentTimeMillis();
while(!finished){
if(System.currentTimeMillis() - currentTime > mills){
System.out.println("超时啦~");
executeThread.interrupt();
break;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
finished = false;
}
public static void main(String[] args) {
ThreadCloseForce threadCloseForce = new ThreadCloseForce();
long start = System.currentTimeMillis();
threadCloseForce.execute(()->{
// while(true){
// //TODO
// }
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadCloseForce.shutdown(10000);
long end = System.currentTimeMillis();
System.out.println("总共消耗时间"+(end-start));
}
}