进程Process VS线程Thread
进程包含线程,多任务既可由多进程实现,也可以由多线程实现。
多进程Process缺点:
1.创建进程Process比线程Thread开销大
2.进程间通信比线程间通信要慢,因为线程间通信就是读写同一个变量,速度快
多进程Process优点:
稳定性高,一个进程崩溃不会影响其他进程,但多线程会因为一个线程崩溃而崩溃
多线程Thread特点
1.多线程模型是Java程序最基本的并发模型
2.读写网络、数据库、Web开发都依赖Java多线程模型
线程Thread状态
Thread状态包含:New、Runnable、Blocked、Waiting、Timed Waiting和Terminated
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
System.out.println("hello");
});
System.out.println("start");
t.start();
t.join(); // 等待线程运行结束
System.out.println("end");
}
如何中断线程
线程执行长时间任务,需要中断线程。中断线程的方式就是其他线程给需要中断的线程发送一个信号,该线程收到信号后结束执行run()方法。使用interrupt()方法来检测是否中断。
public class ThreadInterrupt {
static class MyThread extends Thread {
public void run() {
int n = 0;
while (!isInterrupted()) { // 检测是否需要中断
n++;
System.out.println("run " + n + " time!");
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = new MyThread();
t.start();
Thread.sleep(1); // 当前线程暂停
t.interrupt(); // 通知子线程中断
t.join();
System.out.println("main thread end");
}
}
运行结果:
run 11 time!
run 12 time!
run 13 time!
run 14 time!
main thread end
标志位boolean running是一个线程间共享的变量。线程间共享变量需要使用volatile关键字标记,确保每个线程都能读取到更新后的变量值。
为什么要对线程间共享的变量用关键字volatile声明?这涉及到Java的内存模型。在Java虚拟机中,变量的值保存在主内存中,但是,当线程访问变量时,它会先获取一个副本,并保存在自己的工作内存中。如果线程修改了变量的值,虚拟机会在某个时刻把修改后的值回写到主内存,但是,这个时间是不确定的!
这会导致如果一个线程更新了某个变量,另一个线程读取的值可能还是更新前的。例如,主内存的变量a = true,线程1执行a = false时,它在此刻仅仅是把变量a的副本变成了false,主内存的变量a还是true,在JVM把修改后的a回写到主内存之前,其他线程读取到的a的值仍然是true,这就造成了多线程之间共享的变量不一致。
因此,volatile关键字的目的是告诉虚拟机:
- 每次访问变量时,总是获取主内存的最新值;
- 每次修改变量后,立刻回写到主内存。
volatile关键字解决的是可见性问题:当一个线程修改了某个共享变量的值,其他线程能够立刻看到修改后的值。
如果我们去掉volatile关键字,运行上述程序,发现效果和带volatile差不多,这是因为在x86的架构下,JVM回写主内存的速度非常快,但是,换成ARM的架构,就会有显著的延迟。