关于Java多线程

进程 & 线程

计算机中的一个任务称为一个进程,进程内部的子任务称为线程

操作系统调度的最小任务单位是线程。Windows、Linux等操作系统都采用抢占式多任务,如何调度线程完全由操作系统决定,程序自己不能决定什么时候执行,以及执行多长时间

多任务既可以由多进程实现,也可以由单进程内的多线程实现,还可以混合多进程+多线程

多线程对比多进程,优点在于

  • 创建线程开销小,尤其是在Windows系统上
  • 线程间通信速度更快,因为可以共享变量

也有一些缺点,比如

  • 稳定性较低,任何一个线程崩溃会直接导致整个进程崩溃

Java多线程模型

Java线程内存模型中,可以将虚拟机内存划分为两部分内存:主内存和线程工作内存,主内存是多个线程共享的内存,线程工作内存是每个线程独享的内存

假装这里有一张图片

在上图中,方法区和堆内存就是主内存区域,而虚拟机栈、本地方法栈以及程序计数器则属于每个线程独享的工作内存

Java内存模型规定所有成员变量都需要存储在主内存中,线程会在其工作内存中保存需要使用的成员变量的拷贝,线程对成员变量的操作(读取和赋值等)都是对其工作内存中的拷贝进行操作

各个线程之间不能互相访问工作内存,线程间变量的传递需要通过主内存来完成

Java线程生命周期

Thread.State枚举类中定义了六种线程状态,分别是NEWRUNNALBEBLOCKEDWAITINGTIMED_WAITINGTERMINATED,其生命周期流程如下

假装这里有一张图片


线程创建方法

方法一:从Thread派生一个自定义类,然后覆写run()方法

方法二:定义一个类,实现Runnable接口的run()方法;创建Thread实例时,将其传入

线程启动方法

调用Thread.start()方法,其内部是调用了private native void start0()方法,该start0()方法是由JVM虚拟机内部的C代码实现的,不是由Java代码实现的

线程设定优先级

调用Thread.setPriority(int n)设定优先级,可选范围1-10,默认是5。优先级高的线程被操作系统调度的优先级较高,但具体调度顺序依然由操作系统决定,我们不能通过设置优先级来保证某个线程一定会被先执行

线程等待

Thread.sleep(long millis)使当前线程等待millis毫秒,等待期间线程处于TIMED_WAITING状态

t.join()使当前线程等待目标线程t结束后继续运行;也可以传入一个long类型的参数,t.join(long millis)表示等待至多millis毫秒后继续运行,超过该时间若目标线程仍未结束,则不再等待;对已经运行结束的线程调用join()方法会立刻返回

线程中断

调用目标线程的interrupt()方法可以向其发送中断请求

  • 如果目标线程处在等待状态,会抛出InterruptedException,需对其捕获处理
  • 如果目标线程处在运行状态,代码可以通过isInterrupted()方法判断状态,作相应逻辑处理

或者改变自定义标志位的值,在代码中通过监测标志位的值来判断是否有中断

守护线程

Java程序入口是由JVM启动main线程,main线程又可以启动其他线程。当所有线程都运行结束时,JVM退出,进程结束。可以通过调用t.setDaemon(true)将目标线程设置为守护线程Daemon Thread,JVM退出时不会考虑守护线程是否结束,即守护线程可以在JVM退出后继续存在

t.setDaemon()方法一定要在t.start()之前调用,一旦启动后,无法再更改为守护线程

需要注意的是,守护线程不能持有任何需要关闭的资源,例如打开文件等,因为JVM退出时,守护线程没有任何机会来关闭文件,这会导致数据丢失


示例

 1public class SimpleThread extends Thread {
 2
 3    @Override
 4    public void run() {
 5        System.out.println("Thread " + this.getName() + " is running ...");
 6
 7        int count = 0;
 8        int interval = 1000;
 9
10        while (!isInterrupted()) {
11            try {
12                Thread.sleep(interval);
13                System.out.println("Thread " + this.getName() + " waited for " + ++count + " seconds");
14            } catch (InterruptedException e) {
15                System.out.println("Thread " + this.getName() + " interrupted");
16            }
17        }
18
19        System.out.println("Thread " + this.getName() + " finished");
20    }
21}
22
23public class SimpleRunnable implements Runnable {
24
25    public volatile boolean running = true;
26
27    @Override
28    public void run() {
29        System.out.println("Thread " + this.toString() + " is running ...");
30
31        int count = 0;
32        int interval = 1000;
33
34        while (running) {
35            try {
36                Thread.sleep(interval);
37                System.out.println("Thread " + this.toString() + " waited for " + ++count + " seconds");
38            } catch (InterruptedException e) {
39                System.out.println("Thread " + this.toString() + " interrupted");
40            }
41        }
42
43        System.out.println("Thread " + this.toString() + " finished");
44    }
45}
46
47public class ThreadLab {
48
49    private void simpleStartThread() {
50        /**
51         * Option 1 : Define a thread class by extending the Thread class
52         */
53        Thread t1 = new SimpleThread();
54
55        /**
56         * Option 2 : Define a thread class by implementing Runnable interface and pass it to Thread class as construction parameter
57         */
58        SimpleRunnable simpleRunnable = new SimpleRunnable();
59        Thread t2 = new Thread(simpleRunnable);
60
61        /**
62         * Start threads
63         */
64        t1.start();
65        t2.start();
66
67        try {
68            Thread.sleep(1000);
69
70            /**
71             * Interrupt thread 1 by invoking interrupt()
72             */
73            t1.interrupt();
74            t1.join();
75
76            /**
77             * Interrupt thread 2 by setting running flag to false
78             */
79            simpleRunnable.running = false;
80            t2.join();
81        } catch (InterruptedException e) {
82            System.out.println("Sub thread interrupted");
83        }
84
85        /**
86         * this is a daemon thread, which will not be terminated when JVM quit
87         */
88        Thread t3 = new SimpleThread();
89        t3.setDaemon(true);
90        t3.start();
91
92        System.out.println("Main thread finished");
93    }
94
95    public static void main(String[] args) {
96        ThreadLab threadLab = new ThreadLab();
97        threadLab.simpleStartThread();
98    }
99}