并发编程 Thread
本文将介绍 Java 中的 Thread,它代表线程。
一、Thread 类
1. 说明
Java 用 Thread 类代表线程,所有的线程对象都必须是 Thread 类或其子类的实例。
2. 构造方法
1 |
|
target:线程的任务;Runnable 实例
name:线程的名字
3. 类方法
1 |
|
获取当前的线程。
二、线程创建
1. 法 1:继承 Thread 类
定义 Thread 的子类,重写
run()
方法创建该类的对象
1 |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class FirstThread extends Thread {
private int i;
@Override
public void run() {
for ( ; i < 100; i++) {
System.out.println(getName() + " " + i);
}
}
}
public class Test {
public static void main(String[] args) {
new FirstThread().start();
new FirstThread().start();
}
}
2. 法 2:实现 Runnable 接口
定义类,实现 Runnable 接口,重写
run()
方法实例化接口实现类
以接口实现类的对象作为 Thread 的参数创建 Thread 的对象
1 |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SecondThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class Test {
public static void main(String[] args) {
SecondThread secondThread = new SecondThread();
new Thread(secondThread).start();
new Thread(secondThread).start();
}
}
3. 优缺点
继承 Thread 类方式访问当前类更简单,因为当前类就是它本身
实现 Runnable 接口方式访问当前类需要借助
Thread.currentThread()
继承 Thread 类方式无法继承其它类,因为它已经继承了 Thread 类
实现 Runnable 接口方式可以继承其它类
继承 Thread 类方式每创建一个线程时,都是创建了一个新的实例,因此只能共享类成员
实现 Runnable 接口方式可以在创建线程时传入同一个对象,因此可以共享实例成员
一般采用实现 Runnable 接口方式
三、线程控制
1. 线程启动
通过调用线程实例的 start()
方法启动线程。
需要注意的是:
- 调用
start()
方法,JVM 会新建线程并调用其中的run()
方法 - 调用
run()
方法,只会在当前线程下简单执行该方法,并不会新建线程
2. 线程等待
Thread 提供了 join()
方法,它能够让一个线程等待另外一个线程完成后再继续进行。如果在线程中调用了另一个线程的 join()
方法,则线程将被堵塞,直至另一个线程执行完毕。
未使用
join()
方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>public class RelaxThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("休息了" + i + "下");
}
}
>}
>public class RunThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("走了" + i + "步");
if (i == 20) {
RelaxThread relaxThread = new RelaxThread();
relaxThread.start();
}
}
}
>}
>public static void main(String[] args) {
RunThread runThread = new RunThread();
runThread.start();
>}使用
join()
方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>public class RelaxThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("休息了" + i + "下");
}
}
>}
>public class RunThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("走了" + i + "步");
if (i == 20) {
RelaxThread relaxThread = new RelaxThread();
relaxThread.start();
relaxThread.join();
}
}
}
>}
>public static void main(String[] args) {
RunThread runThread = new RunThread();
runThread.start();
>}
3. 线程休眠
1 |
|
将当前线程设置为堵塞状态。
当线程被 “sleep” 之后,在其睡眠时间段内,它都不会获得执行的机会。
4. 线程让步
1 |
|
当 yield()
被调用后,线程便会让出 CPU 执行权,转为就绪状态,然后与其它线程一同争夺 CPU 执行权。
5. 线程中断
具体请看:
6. 设置为后台线程
有一种线程,它运行于后台,为其它线程提供服务,这种线程被称为“后台线程”。后台线程有一个特征:如果所有的前台线程死亡,后台线程也将自动死亡。
调用 Thread 的 setDaemon()
方法,该方法需要一个 Boolean 类型的参数,填入 true ,即可将指定的线程设置为后台线程。
需要注意的是,当前台线程死亡以后,后台线程并非立即死亡,其接收指令到做出响应也需要一定的时间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class BackEndThread extends Thread {
@Override
public void run() {
while(true) {
System.out.println("后台");
}
}
}
public class TestThread extends Thread {
@Override
public void run() {
BackEndThread backEndThread = new BackEndThread();
backEndThread.setDaemon(true);
backEndThread.start();
for (int i = 0; i < 99; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) {
TestThread testThread = new TestThread();
testThread.start();
}
7. 设置线程优先级
每个线程都具有优先级,在争夺 CPU 的执行权时,优先级高的线程可以获得更多的执行机会。
每个线程线程默认的优先级与创建它的父线程的优先级相同。Thread 类提供了 setPriority(num)
和 getPriority()
方法,用于设置和获取指定线程的优先级。
需要注意的是,优先级高的线程可以获得更多的执行机会,优先被执行,而不是一定先执行。
默认优先级 NORM_PRIORITY:5
最大优先级 MAX_PRIORITY:10
最小优先级 MIN_PRIORITY:1
参考
- Java 并发编程实战