一、并发编程简介
1、基础概念
- 程序
与计算机系统操作有关的计算机程序、规程、规则,以及可能有的文件、文档及数据。
- 进程
进程是计算机中的程序,关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
- 线程
线程是操作系统能够进行运算调度的最小单位,包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
- 顺序编程
程序中的所有步骤在任意时刻只能执行一个步骤。编程中绝大部分场景都是基于顺序编程。
- 并发编程
在一台处理器上“同时”处理多个任务,并行处理程序中的复杂耗时任务。并发是在同一实体上的多个事件。多个事件在同一时间间隔发生。
2、入门案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 1public class HelloThread {
2 public static void main(String[] args) {
3 System.out.println("Hello,Thread");
4 // 当前线程名称
5 System.out.println(Thread.currentThread().getName());
6 // 线程系统的管理接口
7 ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
8 long[] threadIds = threadMXBean.getAllThreadIds() ;
9 for (long id : threadIds) {
10 ThreadInfo threadInfo = threadMXBean.getThreadInfo(id) ;
11 System.out.println(threadInfo.getThreadId()+
12 ":"+threadInfo.getThreadName());
13 }
14 }
15}
16
打印结果:
1
2
3
4
5
6 15:Monitor Ctrl-Break
24:Signal Dispatcher
33:Finalizer
42:Reference Handler
51:main
6
由此可知上述一段简单的Java程序,不止一条main线程在执行。
二、线程创建方式
1、继承Thread类
Thread类的基础结构:
1
2 1class Thread implements Runnable
2
这里已经实现了Runnable接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 1public class CreateThread01 {
2 public static void main(String[] args) {
3 // 调用方法
4 MyThread1 myThread1 = new MyThread1() ;
5 myThread1.start();
6 }
7}
8class MyThread1 extends Thread {
9 // 设置线程名称
10 public MyThread1 (){
11 super("CicadaThread");
12 }
13 @Override
14 public void run() {
15 System.out.println(Thread.currentThread().getName());
16 }
17}
18
2、实现Runnable接口
如果创建的线程类已经存在父类,则不能再继承Thread类,在Java中不允许多继承,这时就可以实现Runnable接口。
1
2
3
4
5
6
7
8
9
10
11
12
13 1public class CreateThread02 {
2 public static void main(String[] args) {
3 Thread thread = new Thread(new MyThread2(),"MyThread2") ;
4 thread.start();
5 }
6}
7class MyThread2 implements Runnable {
8 @Override
9 public void run() {
10 System.out.println(Thread.currentThread().getName()+" run ...");
11 }
12}
13
3、匿名内部类
在一个类里面定义一个类,称为内部类。内部类就相当于外部类的一个成员,可以把内部类看成一个整体。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 1public class CreateThread03 {
2 public static void main(String[] args) {
3 //方式1
4 new Thread("ThreadName1") {
5 public void run() {
6 System.out.println("1:"+Thread.currentThread().getName());
7 };
8 }.start();
9
10 //方式2
11 new Thread(new Runnable() {
12 public void run() {
13 System.out.println("2:"+Thread.currentThread().getName());
14 }
15 },"ThreadName2"){
16 // 这里重写了run方法
17 @Override
18 public void run() {
19 System.out.println("3:"+Thread.currentThread().getName());
20 }
21 }.start();
22 }
23}
24
4、返回值线程
顾名思义,该线程线程异步执行后,可以返回线程的处理结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 1public class CreateThread04 {
2 public static void main(String[] args) throws Exception {
3 MyThread4 myThread4 = new MyThread4();
4 FutureTask<Integer> task = new FutureTask<>(myThread4);
5 Thread thread = new Thread(task,"TaskThread");
6 thread.start();
7 // 等待获取结果
8 // Integer result = task.get();
9 // 设置获取结果的等待时间,超时抛出:TimeoutException
10 Integer result = task.get(3, TimeUnit.SECONDS) ;
11 System.out.println("result="+result);
12 }
13}
14class MyThread4 implements Callable<Integer> {
15 // 封装线程执行的任务
16 @Override
17 public Integer call() throws Exception {
18 System.out.println(Thread.currentThread().getName());
19 Thread.sleep(1000);
20 return 2+3;
21 }
22}
23
5、定时任务
Timer是后台线程执行任务调度的工具类,可以根据规则配置定期执行或者重复执行。
1
2 1class TimerTask implements Runnable
2
任务类:TimerTask结构实现Runnable接口。
1
2
3
4
5
6
7
8
9
10
11
12 1public class CreateThread05 {
2 public static void main(String[] args) {
3 Timer timer = new Timer();
4 timer.schedule(new TimerTask() {
5 @Override
6 public void run() {
7 System.out.println("延迟1s,每隔3s执行一次");
8 }
9 }, 1000, 3000);
10 }
11}
12
6、线程池管理
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1public class CreateThread06 {
2 public static void main(String[] args) {
3 Executor threadPool = Executors.newFixedThreadPool(5);
4 for(int i = 0 ;i < 5 ; i++) {
5 threadPool.execute(new Runnable() {
6 @Override
7 public void run() {
8 System.out.println(Thread.currentThread().getName());
9 }
10 });
11 }
12 }
13}
14
三、线程状态管理
1、状态描述
- NEW
初始状态:构建线程实例后,调用start()方法启动前,处于该状态。
- RUNNABLE
运行状态:在Java线程中,就绪和运行两个状态称作运行状态,在实际的执行过程中,这两个状态是随时可能切换的。启动start()方法被调用,或者sleep()后,join()结束等,就进入RUNNABLE就绪状态,开始等待CPU时间片;线程调度选中该线程、并分配了CPU时间片后,该线程尽管处于Runnable状态,就是运行状态(Running);
- BLOCKED
阻塞状态:通常指被锁机制阻塞,表示线程正在获取有锁控制的资源。
- WAITING
等待状态:进入该状态的线程,等待被其他线程发出通知或中断,也称显式唤醒。
- TIMED_WAITING
超时等待状态:该状态不同于WAITING状态,该状态的线程可以在指定的时间后自动唤醒;
- TERMINATED
终止状态:表示当前线程任务执行完毕。
2、案例流程分析
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 1public class StateCycle01 {
2 public static void main(String[] args) throws Exception {
3 // 进入初始状态
4 StateThread01 stateThread01 = new StateThread01();
5 FutureTask<String> task = new FutureTask<>(stateThread01);
6 Thread thread = new Thread(task,"GetValueThread");
7 // 运行状态
8 thread.start();
9 // 超时等待结果
10 String result = task.get(3, TimeUnit.SECONDS) ;
11 System.out.println("result="+result);
12
13 StateThread02 stateThread02 = new StateThread02() ;
14 Thread thread1 = new Thread(stateThread02,"WaitThread");
15 thread1.start();
16 }
17}
18class StateThread01 implements Callable<String> {
19 @Override
20 public String call() throws Exception {
21 // 超时等待
22 Thread.sleep(1000);
23 return "Hello,Cicada";
24 }
25}
26class StateThread02 implements Runnable {
27 @Override
28 public void run() {
29 synchronized (StateCycle01.class) {
30 System.out.println("进入线程...");
31 try {
32 // 等待状态,放弃对象锁
33 StateCycle01.class.wait(2000);
34 } catch (Exception e) {
35 e.printStackTrace();
36 }
37 System.out.println("线程继续...");
38 }
39 }
40}
41
上述流程描述了线程不同状态之间的切换,基本流程图如下。
线程的状态描述起来不算复杂,但是每个状态间的切换,是非常的复杂,后续会分模块单个解释。
四、优缺点总结
1、优点说明
最直接作用使程序执行的效率大幅度提升;程序异步解耦,在web开发中,经常有后续的程序要执行,有需要快速的用户界面响应;当然熟练使用并发编程,也是一个优秀程序员必备技能 。
2、缺点分析
并发编程学习的曲线非常陡峭,难度较大;多线程之间争抢资源容易出现问题;并不是线程越多,执行速度就越快,线程之前切换是耗时的,需要合理创建和使用锁机制;线程创建和之间的通信需要很清晰的逻辑;线程死锁问题更是无法完全避免的问题;所以在一般情况下公司对线程使用的规范是十分严格的。
五、源代码地址
1
2
3
4
5 1GitHub·地址
2https://github.com/cicadasmile/java-base-parent
3GitEE·地址
4https://gitee.com/cicadasmile/java-base-parent
5