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