前言
在操作系统中有两个概念叫进程(process)和线程(thread),这两个概念,比较抽象,涉及的知识非常多。鉴于篇幅本文只能从一些角度来解释。
进程 (process)
进程是计算机中已运行程序的实体。进程为曾经是分时系统的基本运作单位。而在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。
在 Windows 系统中,一个 QQ.exe 就是一个进程,一个 JAVA.exe 就是一个进程。
线程(thread)
线程是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。
在 Java.exe 可以执行多个线程,后续会介绍创建线程的方式。
线程与进程的区别
- 一个程序至少有一个进程,一个进程至少有一个线程。
- 线程的划分尺度小于进程,使得多线程程序的并发性高。
- 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
- 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 多线程在一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。
- 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号),某进程内的线程在其它进程不可见
线程的状态
- 新建状态(New):新创建了一个线程对象,当你用 new 创建一个线程时,该线程尚未运行。
- 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的 start() 方法。该状态的线程位于可运行线程池中,变得可运行,等待获取 CPU 的使用权。
- 运行状态(Running):就绪状态的线程获取了 CPU ,执行程序代码。
- 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃 CPU 使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
- 等待阻塞:运行的线程执行 wait() 方法,JVM 会把该线程放入等待池中。
- 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 把该线程放入锁。
- 其他阻塞:运行的线程执行sleep() 或 join() 方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep() 状态超时、join() 等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 死亡状态(Dead):
- 由于 run 方法的正常退出而自然死亡;
- 没有捕获到的异常事件终止了 run 方法的执行,从而导致线程突然死亡
Java 多线程的实现方式
常见的主要有两种
1、继承 Thread 类
2、实现 Runnable 接口
继承 Thread 类的方式
代码如下1
2
3
4
5
6
7
8
9
10public class ThreadByExtendsThread extends Thread {
public void run() {
System.out.println("This a Thread ---- BY extends Thread");
}
public static void main(String[] args) {
new ThreadByExtendsThread().start();
}
}
实现 Runnable 接口的方式
代码如下1
2
3
4
5
6
7
8
9
10public class ThreadByImplementsRunnable implements Runnable {
public void run() {
System.out.println("This a Thread ---- BY implements Runnable");
}
public static void main(String[] args) {
new Thread(new ThreadByImplementsRunnable()).start();
}
}
个人比较倾向于使用实现 Runnable 接口方式,因为 Java 是单继承的。
线程运行 (start)
新线程创建之后,你调用它的 start() 方法它才会运行。
1 | void start(); |
例如:1
2
3
4
5
6
7
8
9
10
11
12/**
* Created by ARNO on 2016/5/27/027.
*/
public class ThreadTest {
public static void main(String[] args) {
Thread threadByImplementsRunnable = new Thread(new ThreadByImplementsRunnable());
threadByImplementsRunnable.start();
Thread threadByExtendsThread = new ThreadByExtendsThread();
threadByExtendsThread.start();
}
}
线程休眠 (sleep)
代码如下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
27package com.doity.threadDemo.Write;
/**
* Created by ARNO on 2016/5/27/027.
*/
public class ThreadSleepDemo extends Thread {
public void run() {
try {
System.out.println("线程thread正在运行!");
System.out.println("线程thread睡眠2秒中...!");
Thread.sleep(2000); //静态方法,使当前正在执行的线程睡眠2秒
System.out.println("线程thread在睡眠后重新运行!");
System.out.printf("线程thread执行完毕");
} catch (InterruptedException e) {
System.out.println("线程被中断");
}
}
public static void main(String[] args) {
System.out.println("启动主线程main");
System.out.println("创建一个线程:thread");
Thread thread = new ThreadSleepDemo();
thread.start();
System.out.println("主线程main结束!");
}
}
运行结果
1 | 启动主线程main |
线程中断(interrupt)
在 Java 提供的线程支持类 Thread 中,有三个用于线程中断的方法:
public void interrupt();
中断线程。
public static boolean interrupted();
是一个静态方法,用于测试当前线程是否已经中断,并将线程的中断状态 清除。所以如果线程已经中断,调用两次 interrupted,第二次时会返回 false,因为第一次返回 true 后会清除中断状态。
public boolean isInterrupted();
测试线程是否已经中断。
方式一
代码如下: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
28
29
30
31package com.doity.threadDemo.Write;
public class ThreadInterruptDemo {
public static void main(String[] args) {
DDoSAttack dDoSAttack = new DDoSAttack();
Thread thread = new Thread(dDoSAttack, "DDoS");
thread.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("有其他状况,先中断攻击.");
System.out.println("中断前的状态:" + thread.isInterrupted());
thread.interrupt(); // 执行中断操作
System.out.println("中断后的状态:" + thread.isInterrupted());
}
}
class DDoSAttack implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + "---正在攻击····");
}
if (Thread.currentThread().isInterrupted()) {
//返回当前线程的状态,并清除状态
System.out.println("interrupted:" + Thread.interrupted());
System.out.println("isInterrupted:" + Thread.currentThread().isInterrupted());
}
}
}
运行结果
1 | DDoS+正在攻击。。。。 |
方式二
1 | package com.doity.threadDemo.Write; |
运行结果1
1 | 在主线程中 i=1 |
运行结果2
1 | 在主线程中 i=1 |
至于为什么会有两种结果,后续篇章再解释。
线程合并(join)
public final void join()
等待该线程终止
代码如下
1 | package com.doity.threadDemo.Write; |
运行结果
1 | 的士司机把车门打开 |
后记
内容整理中,待续。