第1章 快速认识线程
1.1 线程的介绍
对于计算机来说每一个任务就是一个进程Process
,在每一个进程内部至少要有一个线程Thread
是在运行中的。
1.2 快速创建并启动一个线程
1.2.1 尝试并行运行
package chapter01;
import java.util.concurrent.TimeUnit;
public class TryConcurrency {
private static void browsNews() {
while (true) {
System.out.println("good news");
sleep(1);
}
}
private static void enjoyMusic() {
while (true) {
System.out.println("good music");
sleep(1);
}
}
private static void sleep(double seconds) {
try {
Thread.sleep((long) (seconds * 1000));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
browsNews();
enjoyMusic();
}
}
输出 :
good news
good news
good news
...
good news
good news
good news
1.2.2 并发运行交替输出
更改一下main方法
new Thread(TryConcurrency::browsNews).start();
enjoyMusic();
或
new Thread(new Runnable() {
@Override
public void run() {
browsNews();
}
}).start();
enjoyMusic();
1.2.3 使用Jconsole观察线程
1.3 线程的生命周期详解
每个线程都有自己的局部变量表,程序计数器,以及生命周期等。
线程的生命周期大体可以分为5个阶段 :
- NEW
- RUNNABLE
- RUNNING
- BLOCKED
- TERMINATED
1.3.1 线程的NEW状态
当你创建了一个Thread
对象,但没有调用start
方法时线程处于NEW
状态。
NEW
状态通过start
方法进入RUNNABLE
状态。
1.3.2 线程的RUNNABLE状态
线程一旦调用start
会立即进入RUNNABLE
状态,但并不会立即执行,线程的运行与否要听命于CPU的调度。
1.3.3 线程的RUNNING状态
一旦CPU通过轮询或者其他方式从任务可执行队列中选中了线程,那么此时它才能真正地执行自己的逻辑代码,需要说明的一点是一个正在RUNNING状态的线程事实上也是RUNNABLE的,但是反过来则不成立。
1.3.4 线程的BLOCKED状态
1.3.5 线程的TERMINATED状态
TERMINATED
是一个线程的最终状态,意味着整个线程的生命周期都结束了。
1.4 线程的start
方法剖析:模板设计模式在Thread
中的应用
当启动一个线程时调用的是start
方法,而被重写的方法是run
方法,二者之间有怎样不为人知的关系呢。
1.4.1 Thread start方法源码分析以及注意事项
这是Thread start
方法的源码
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
} }
}
start
方法内部会调用private native void start0();
这个本地方法
Thread
被构造后的NEW
状态,由threadStatus
这个内部属性设置为0来表示- 不能启动两次
Thread
,否则抛出IllegalThreadStateException
异常 - 当线程启动后会被加入到
ThreadGroup
中。 - 当线程的生命周期结束后,也就是
TERMINATED
状态,再次调用start
方法也是不被允许的
连续两次启动同一线程
package chapter01;
import java.util.concurrent.TimeUnit;
public class Test04 {
public static void main(String[] args) {
// 匿名内部类
Thread thread = new Thread() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
thread.start();
// 两次启动同一线程会抛出异常
thread.start();
}
}
输出 :
Exception in thread "main" java.lang.IllegalThreadStateException
at java.base/java.lang.Thread.start(Thread.java:802)
at chapter01.Test04.main(Test04.java:19)
在线程结束后再次启动线程
package chapter01;
import java.util.concurrent.TimeUnit;
public class Test04 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
thread.start();
// 确保线程已经结束
TimeUnit.SECONDS.sleep(2);
thread.start();
}
}
输出 :
Exception in thread "main" java.lang.IllegalThreadStateException
at java.base/java.lang.Thread.start(Thread.java:802)
at chapter01.Test04.main(Test04.java:20)
也会抛出IllegalThreadStateException
异常。
1.4.2 模板设计模式在Thread中的应用
线程的真正执行逻辑是在run
方法中的,通常把run
方法称为线程的执行单元。
其实Thread
的start
和run
是一个典型的模板设计模式,父类编写算法结构代码,子类实现逻辑细节。
下面是一个简易的模板设计模式的代码
package chapter01;
public class TemplateMethod {
public final void print(String msg) {
System.out.println("--------------------");
wrapPrint(msg);
System.out.println("--------------------");
}
public void wrapPrint(String msg) {
}
public static void main(String[] args) {
new TemplateMethod() {
@Override
public void wrapPrint(String msg) {
System.out.println("Hello " + msg + "Hello");
}
}.print("你好");
System.out.println();
new TemplateMethod() {
@Override
public void wrapPrint(String msg) {
System.out.println("nihk " + msg + "nihk");
}
}.print("你好");
}
}
输出 :
--------------------
Hello 你好Hello
--------------------
--------------------
nihk 你好nihk
--------------------
1.4.3 Thread 模拟营业大厅叫号机程序
启动四个线程售票
package chapter01;
import java.util.concurrent.TimeUnit;
public class TicketWindows extends Thread {
private final String name;
private static final int MAX = 50;
private static int index = 1;
public TicketWindows(String name) {
this.name = name;
}
@Override
public void run() {
while (index <= MAX) {
System.out.println("柜台 : " + name + "当前的号码 : " + (index++));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
// 开启四个窗口
new TicketWindows("anan").start();
new TicketWindows("jcjc").start();
new TicketWindows("yjyj").start();
new TicketWindows("yryr").start();
}
}
1.5 Runnable接口的引入以及策略模式在Thread中的使用
1.5.1 Runnable的职责
Runnable
接口非常简单,只定义了一个无参数无返回值的run
方法。
public interface Runnable {
public abstract void run();
}
创建线程只有一种方式就是构造Thread
类,而实现线程的执行单元则有两种方式,第一种是重写Thread
的run
方法,第二种是实现Runnable
接口的run
方法,并将Runnable
实例用作构造Thread
的参数。
1.5.2 策略模式在Thread中的应用
1.5.3 模拟营业大厅叫号机程序
其实就是用Runnable
接口重写了一下之前直接继承Thread
的写法。