[JAVA]线程与进程总结(一)

前言

在操作系统中有两个概念叫进程(process)和线程(thread),这两个概念,比较抽象,涉及的知识非常多。鉴于篇幅本文只能从一些角度来解释。

进程 (process)

进程是计算机中已运行程序的实体。进程为曾经是分时系统的基本运作单位。而在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。

在 Windows 系统中,一个 QQ.exe 就是一个进程,一个 JAVA.exe 就是一个进程。

线程(thread)

线程是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。

在 Java.exe 可以执行多个线程,后续会介绍创建线程的方式。

线程与进程的区别

  1. 一个程序至少有一个进程,一个进程至少有一个线程。
  2. 线程的划分尺度小于进程,使得多线程程序的并发性高。
  3. 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
  4. 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  5. 多线程在一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。
  6. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号),某进程内的线程在其它进程不可见

线程的状态

  1. 新建状态(New):新创建了一个线程对象,当你用 new 创建一个线程时,该线程尚未运行。
  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的 start() 方法。该状态的线程位于可运行线程池中,变得可运行,等待获取 CPU 的使用权。
  3. 运行状态(Running):就绪状态的线程获取了 CPU ,执行程序代码。
  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃 CPU 使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    • 等待阻塞:运行的线程执行 wait() 方法,JVM 会把该线程放入等待池中。
    • 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 把该线程放入锁。
    • 其他阻塞:运行的线程执行sleep() 或 join() 方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep() 状态超时、join() 等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  5. 死亡状态(Dead):
    • 由于 run 方法的正常退出而自然死亡;
    • 没有捕获到的异常事件终止了 run 方法的执行,从而导致线程突然死亡

Java 多线程的实现方式

常见的主要有两种
1、继承 Thread 类
2、实现 Runnable 接口

继承 Thread 类的方式

代码如下

1
2
3
4
5
6
7
8
9
10
public class ThreadByExtendsThread extends Thread {
@Override
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
10
public class ThreadByImplementsRunnable implements Runnable {
@Override
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
27
package com.doity.threadDemo.Write;

/**
* Created by ARNO on 2016/5/27/027.
*/

public class ThreadSleepDemo extends Thread {
@Override
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
2
3
4
5
6
7
启动主线程main
创建一个线程:thread
主线程main结束!
线程thread正在运行!
线程thread睡眠2秒中...!
线程thread在睡眠后重新运行!
线程thread执行完毕

线程中断(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
31
package 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DDoS+正在攻击。。。。
DDoS+正在攻击。。。。
DDoS+正在攻击。。。。
DDoS+正在攻击。。。。
DDoS+正在攻击。。。。
DDoS+正在攻击。。。。
DDoS+正在攻击。。。。
DDoS+正在攻击。。。。
有其他状况,先中断攻击.
DDoS+正在攻击。。。。
DDoS+正在攻击。。。。
中断前的状态:false
DDoS+正在攻击。。。。
中断后的状态:true
interrupted:true
isInterrupted:false

方式二

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
31
32
33
34
package com.doity.threadDemo.Write;

public class ThreadShutDownDemo {

public static void main(String args[]) {
Runner runner = new Runner();
Thread thread = new Thread(runner);
thread.start();
for (int i = 1; i < 10; i++) {
System.out.println("在主线程中 i=" + i);
}
System.out.println("主线程main结束");
//通知线程结束
runner.shutDown();
}
}

class Runner implements Runnable {

private boolean keepRunnning = true;

public void run() {
int i = 0;
while (keepRunnning == true) {
System.out.println("在子线程中 i=" + i++);
}
System.out.println("子线程结束");
}

// 设置线程结束方法
public void shutDown() {
keepRunnning = false;
}
}

运行结果1

1
2
3
4
5
6
7
8
9
10
11
在主线程中 i=1
在主线程中 i=2
在主线程中 i=3
在主线程中 i=4
在主线程中 i=5
在主线程中 i=6
在主线程中 i=7
在主线程中 i=8
在主线程中 i=9
主线程main结束
子线程结束

运行结果2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在主线程中 i=1
在子线程中 i=0
在主线程中 i=2
在子线程中 i=1
在主线程中 i=3
在子线程中 i=2
在主线程中 i=4
在子线程中 i=3
在主线程中 i=5
在子线程中 i=4
在主线程中 i=6
在子线程中 i=5
在主线程中 i=7
在子线程中 i=6
在主线程中 i=8
在子线程中 i=7
在主线程中 i=9
在子线程中 i=8
主线程main结束
在子线程中 i=9
子线程结束

至于为什么会有两种结果,后续篇章再解释。

线程合并(join)

public final void 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.doity.threadDemo.Write;

/**
* Created by HiArno on 2016/5/27.
*/

public class ThreadJoinDemo {
public static void main(String[] args) {
System.out.println("的士司机把车门打开");
Thread thread1 = new Thread(new OldMan());
Thread thread2 = new Thread(new Children());
try {
thread1.start(); //
thread1.join(); // 等该线程执行完毕,才会启动 thread2
thread2.start();
thread2.join(); //等该线程执行完毕以后才能回到主线程

} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("司机将车门关好,开始开车");
}
}

class Children implements Runnable {

@Override
public void run() {
System.out.println("儿童先上车.");
System.out.println("上车中...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("儿童上车完毕.");
}
}

class OldMan implements Runnable {

@Override
public void run() {
System.out.println("老人先上车.");
System.out.println("安装中...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("老人上车完毕.");
}
}

运行结果

1
2
3
4
5
6
7
8
的士司机把车门打开
老人先上车.
安装中...
老人上车完毕.
儿童先上车.
上车中...
儿童上车完毕.
司机将车门关好,开始开车

后记

内容整理中,待续。

参考资料