新闻资讯

新闻资讯 行业动态

Java并发编程(01):线程的创建方式,状态周期管理

编辑:006     时间:2020-03-05

一、并发编程简介

1、基础概念

  • 程序

与计算机系统操作有关的计算机程序、规程、规则,以及可能有的文件、文档及数据。

  • 进程

进程是计算机中的程序,关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

  • 线程

线程是操作系统能够进行运算调度的最小单位,包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

  • 顺序编程

程序中的所有步骤在任意时刻只能执行一个步骤。编程中绝大部分场景都是基于顺序编程。

  • 并发编程

在一台处理器上“同时”处理多个任务,并行处理程序中的复杂耗时任务。并发是在同一实体上的多个事件。多个事件在同一时间间隔发生。

2、入门案例

  1. public 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. }

打印结果:

  1. 5:Monitor Ctrl-Break
  2. 4:Signal Dispatcher
  3. 3:Finalizer
  4. 2:Reference Handler
  5. 1:main

由此可知上述一段简单的Java程序,不止一条main线程在执行。

二、线程创建方式

1、继承Thread类

Thread类的基础结构:

  1. class Thread implements Runnable

这里已经实现了Runnable接口。

  1. public class CreateThread01 {
  2. public static void main(String[] args) {
  3. // 调用方法
  4. MyThread1 myThread1 = new MyThread1() ;
  5. myThread1.start();
  6. }
  7. }
  8. class 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. }

2、实现Runnable接口

如果创建的线程类已经存在父类,则不能再继承Thread类,在Java中不允许多继承,这时就可以实现Runnable接口。

  1. public class CreateThread02 {
  2. public static void main(String[] args) {
  3. Thread thread = new Thread(new MyThread2(),"MyThread2") ;
  4. thread.start();
  5. }
  6. }
  7. class MyThread2 implements Runnable {
  8. @Override
  9. public void run() {
  10. System.out.println(Thread.currentThread().getName()+" run ...");
  11. }
  12. }

3、匿名内部类

在一个类里面定义一个类,称为内部类。内部类就相当于外部类的一个成员,可以把内部类看成一个整体。

  1. public 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. //方式2
  10. new Thread(new Runnable() {
  11. public void run() {
  12. System.out.println("2:"+Thread.currentThread().getName());
  13. }
  14. },"ThreadName2"){
  15. // 这里重写了run方法
  16. @Override
  17. public void run() {
  18. System.out.println("3:"+Thread.currentThread().getName());
  19. }
  20. }.start();
  21. }
  22. }

4、返回值线程

顾名思义,该线程线程异步执行后,可以返回线程的处理结果。

  1. public 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. }
  14. class 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. }

5、定时任务

Timer是后台线程执行任务调度的工具类,可以根据规则配置定期执行或者重复执行。

  1. class TimerTask implements Runnable

任务类:TimerTask结构实现Runnable接口。

  1. public 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. }

6、线程池管理

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。

  1. public 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. }

三、线程状态管理

1、状态描述

  • NEW

初始状态:构建线程实例后,调用start()方法启动前,处于该状态。

  • RUNNABLE

运行状态:在Java线程中,就绪和运行两个状态称作运行状态,在实际的执行过程中,这两个状态是随时可能切换的。启动start()方法被调用,或者sleep()后,join()结束等,就进入RUNNABLE就绪状态,开始等待CPU时间片;线程调度选中该线程、并分配了CPU时间片后,该线程尽管处于Runnable状态,就是运行状态(Running);

  • BLOCKED

阻塞状态:通常指被锁机制阻塞,表示线程正在获取有锁控制的资源。

  • WAITING

等待状态:进入该状态的线程,等待被其他线程发出通知或中断,也称显式唤醒。

  • TIMED_WAITING

超时等待状态:该状态不同于WAITING状态,该状态的线程可以在指定的时间后自动唤醒;

  • TERMINATED

终止状态:表示当前线程任务执行完毕。

2、案例流程分析

  1. public 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. StateThread02 stateThread02 = new StateThread02() ;
  13. Thread thread1 = new Thread(stateThread02,"WaitThread");
  14. thread1.start();
  15. }
  16. }
  17. class StateThread01 implements Callable<String> {
  18. @Override
  19. public String call() throws Exception {
  20. // 超时等待
  21. Thread.sleep(1000);
  22. return "Hello,Cicada";
  23. }
  24. }
  25. class StateThread02 implements Runnable {
  26. @Override
  27. public void run() {
  28. synchronized (StateCycle01.class) {
  29. System.out.println("进入线程...");
  30. try {
  31. // 等待状态,放弃对象锁
  32. StateCycle01.class.wait(2000);
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. System.out.println("线程继续...");
  37. }
  38. }
  39. }

上述流程描述了线程不同状态之间的切换,基本流程图如下。

线程的状态描述起来不算复杂,但是每个状态间的切换,是非常的复杂,后续会分模块单个解释。

四、优缺点总结

1、优点说明

最直接作用使程序执行的效率大幅度提升;程序异步解耦,在web开发中,经常有后续的程序要执行,有需要快速的用户界面响应;当然熟练使用并发编程,也是一个优秀程序员必备技能 。

2、缺点分析

并发编程学习的曲线非常陡峭,难度较大;多线程之间争抢资源容易出现问题;并不是线程越多,执行速度就越快,线程之前切换是耗时的,需要合理创建和使用锁机制;线程创建和之间的通信需要很清晰的逻辑;线程死锁问题更是无法完全避免的问题;所以在一般情况下公司对线程使用的规范是十分严格的。

五、源代码地址

  1. GitHub·地址
  2. https://github.com/cicadasmile/java-base-parent
  3. GitEE·地址
  4. https://gitee.com/cicadasmile/java-base-parent

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/69957347/viewspace-2678571/,如需转载,请注明出处,否则将追究法律责任。

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

回复列表

相关推荐