Thread 类的每一个实例都表示一个线程, 进程是操作系统级别的多任务,JVM 就是运行在一个进程中的,所以在Java 中我我们只考虑线程
# 线程状态
1.new :当我们new 一个线程时,该线程并没有纳入线程调度,而是处于一个new状态。
2.runnable: 当调用线程的start方法后,该线程纳入线程调度的控制,其处于一个可运行状态,等待分配时间片段以并发运行。
3.running:当线程被分配时间片段后其被cpu执行,这是该线程处于running状态。
4.blocked: 当线程在运行过程中可能会出现阻塞现象,比如等待用户输入信息等。但阻塞状态不是百分百出现的,具体要看代码中是否有相关需求。
5.Dead: 当线程的任务全部运行完毕,或在运行过程中抛出了一个未捕获的异常,那么线程结束,等待GC回收
# 创建线程
创建线程有两种方式
方式1:定义一个类并继承Thread,然后重写run方法,在其中书写线程任务逻辑:
Thread t1 = new MyThread1();
Thread t2 = new MyThread2();
t1.start();
t2.start();
class MyThread1 extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("are you ok?");
}
}
}
class MyThread2 extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("of course");
}
}
}
启动线程要调用start方法,不能直接调用run方法。start方法会将当前线程纳入到线程调度中,使其具有并发运行的能力。start方法很快会执行完毕。当start方法执行完毕后,当前线程的run方法会很快的被执行起来(只要获取到了cpu时间片)。但不能理解为调用start方法时run方法就执行了!
线程有几个不可控因素:
-
cpu分配时间片给哪个线程我们说了不算。
-
时间片长短也不可控。
-
线程调度会尽可能均匀的将时间片分配给多个线程。
第一种创建线程的方式存在两个不足:
-
由于java是单继承的,这就导致我们若继承了Thread类就无法再继承其他类,这在写项目时会遇到很大问题;
-
由于我们定义线程的同时重写run方法来定义线程要执行的任务,这就导致线程与任务有一个强耦合关系,线程的重用性变得非常局限。
方式 2:定义一个类并实现Runnable接口然后在创建线程的同时将任务指定。因为是实现Runnable接口,所以不影响其继承其他类:
1
2
3
4
5
6
7
8
9
10
11
12
13Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("are you ok?");
}
});
t1.start();
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("of course");
}
});
t2.start();