进程

概念

进程就是正在运行的程序,它代表了程序所占用的内存区域

特点

  1. 独立性
    进程是系统中独立存在的实体,它可以拥有自己独立的资源,每个进程都拥有自己私有的地址空间,在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间
  2. 动态性

进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合,程序加入了时间的概念以后,称为进程,具有自己的生命周期和各种不同的状态,这些概念都是程序所不具备的.

  1. 并发性
    多个进程可以在单个处理器CPU上并发执行,多个进程之间不会互相影响.

并行与并发

  • 并发:多个进程抢占CPU
  • 并行:多个进程都有自己的CPU进行处理,无抢占现象。

线程

概念

线程是操作系统OS能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.
一个进程可以开启多个线程,其中有一个主线程来调用本进程中的其他线程

多线程扩展了多进程的概念,使的同一个进程可以同时并发处理多个任务,每个线程拥有自己独立的内存空间,多线程之间也存在共享数据。

同类的多个线程共享进程的方法区资源,但每个线程有自己的程序计数器虚拟机栈本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

进程与线程

一个操作系统中可以有多个进程,一个进程中可以包含一个线程(单线程程序),也可以包含多个线程(多线程程序)

多线程特性

  1. 随机性
    线程的随机性指的是同一时刻,只有一个程序在执行
    宏观:多个进程/线程同时运行
    微观:一个CPU同一时刻只能处理一个任务
  2. 分时调度:
    时间片,即CPU分配给各个线程的一个时间段,称作它的时间片,即该线程被允许运行的时间,在时间片用完时线程还在执行,那CPU将被剥夺并分配给另一个线程。将当前线程挂起,如果线程在时间片用完之前阻塞或结束,则CPU当即进行切换,从而避免CPU资源浪费,当再次切换到之前挂起的线程,恢复现场,继续执行。

三态模型

  • 就绪(可运行)状态:线程已经准备好运行,只要获得CPU,就可立即执行
  • 执行(运行)状态:线程已经获得CPU,其程序正在运行的状态
  • 阻塞状态:正在运行的线程由于某些事件(I/O请求等)暂时无法执行的状态,即线程执行阻塞

image-20210721111745306.png

就绪 → 执行:为就绪线程分配CPU即可变为执行状态”
执行 → 就绪:正在执行的线程由于时间片用完被剥夺CPU暂停执行,就变为就绪状态
执行 → 阻塞:由于发生某事件,使正在执行的线程受阻,无法执行,则由执行变为阻塞,如果获得了之前需要的资源,则由阻塞变为就绪状态,等待分配CPU再次执行

五态模型

  • 创建状态:线程的创建比较复杂,需要先申请PCB,然后为该线程运行分配必须的资源,并将该线程转为就绪状态插入到就绪队列中
  • 终止状态:等待OS进行善后处理,最后将PCB清零,并将PCB返回给系统

image-20210721111951564.png

PCB(Process Control Block):为了保证参与并发执行的每个线程都能独立运行,OS配置了特有的数据结构PCB来描述线程的基本情况和活动过程,进而控制和管理线程

线程生命周期

  1. 新建状态(New) : 当线程对象创建后就进入了新建状态.如:Thread t = new MyThread();
  2. 就绪状态(Runnable):当调用线程对象的start()方法,线程即为进入就绪状态.
    处于就绪(可运行)状态的线程,只是说明线程已经做好准备,随时等待CPU调度执行,并不是执行了t.start()此线程立即就会执行
  3. 运行状态(Running):当CPU调度了处于就绪状态的线程时,此线程才是真正的执行,即进入到运行状态a
    就绪状态是进入运行状态的唯一入口,也就是线程想要进入运行状态状态执行,先得处于就绪状态
  4. 阻塞状态(Blocked):处于运状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会被CPU选中再次执行.
    根据阻塞状态产生的原因不同,阻塞状态又可以细分成三种:
    1. 等待阻塞:运行状态中的线程执行wait()方法,本线程进入到等待阻塞状态
    2. 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态
    3. 其他阻塞:调用线程的sleep()或者join()或发出了I/O请求时,线程会进入到阻塞状态.当sleep()状态超时.join()等待线程终止或者超时或者I/O处理完毕时线程重新转入就绪状态
  1. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期

多线程实现方式

继承Thread()方法

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。
启动线程的唯一方法就是通过Thread类的start()实例方法。
start()方法是一native方法,它将通知底层操作系统,.最终由操作系统启动一个新线程,操作系统将执行run()。
这种方式实现的多线程很简单,通过自己的类直接extends Thread,并重写run()方法,就可以自动启动新线程并执行自己定义的run()方法。
模拟开启多个线程,每个线程调用run()方法。

常用方法:

构造方法

方法 作用
Thread(Runnable target) 分配一个新的 Thread
对象。
Thread(Runnable target, String name) 分配一个新的 Thread
对象。
Thread(String name) 分配一个新的 Thread
对象。
Thread() 分配一个新的 Thread
对象。

普通方法

返回值类型 方法名
static Thread currentThread()
返回对当前正在执行的线程对象的引用。
long getId() 返回该线程的标识
String getName() 返回该线程的名称
void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法
static void sleep(long millions) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
void start() 使该线程开始执行:Java虚拟机调用该线程的run()

示例代码

  1. package cn.tedu.thread;
  2. /**
  3. * 多线程实现方式
  4. * 线程随机性:多个线程对象执行效果不可控,由CPU调度处理,结果具有随机性
  5. * 哪个时间片执行哪个线程以及时间片有多长,只能由CPU控制,无法人为调控
  6. */
  7. public class TestThread {
  8. public static void main(String[] args) {
  9. MyThread m = new MyThread();//对应线程为新建状态
  10. MyThread m1 = new MyThread();//对应线程为新建状态
  11. MyThread m2 = new MyThread();//对应线程为新建状态
  12. MyThread m3 = new MyThread();//对应线程为新建状态
  13. MyThread m4 = new MyThread("马钊");//对应线程为新建状态
  14. /*
  15. 使用run()方法,普通方法的调用
  16. 没有多线程的效果
  17. m.run();
  18. m1.run();
  19. */
  20. m.start();//start()方法会把线程加入就绪队列,以多线程的方式调用
  21. m1.start();
  22. m2.start();
  23. m3.start();
  24. m4.start();
  25. }
  26. }
  27. class MyThread extends Thread{
  28. /*
  29. 线程中的业务必须写在run()里,不执行父类的run(),执行自己的业务
  30. */
  31. @Override
  32. public void run() {
  33. /*
  34. 打印10次当前正在执行的线程名称
  35. getName()可以获取当前正在执行的线程名称
  36. 由父类继承而来,可以直接调用
  37. */
  38. for (int i = 0;i < 10;i++){
  39. System.out.println("第" + (i+1) + "个" + getName());
  40. }
  41. }
  42. public MyThread() {
  43. }
  44. public MyThread(String name) {
  45. /*
  46. 子类对象构造方法触发父类构造方法来给线程起名
  47. */
  48. super(name);
  49. }
  50. }

实现Runnable接口

概述

如果类已经extends,另一个类,就无法多继承,此时,此时可以实现一个Runnable接口

常用方法

void run()使用实现接口Runnable的对象创建线程时,启动该线程将导致在独立执行的线程中调用对象的run()方法

示例代码

  1. package cn.tedu.thread;
  2. /**
  3. * 多线程实现方式
  4. */
  5. public class TestRunnable {
  6. public static void main(String[] args) {
  7. MyRunnable m = new MyRunnable();
  8. //将接口实现类对象与Thread建立关系
  9. Thread thread = new Thread(m,"马钊");
  10. Thread thread1 = new Thread(m,"桂宏宇");
  11. Thread thread2 = new Thread(m,"雨来");
  12. Thread thread3 = new Thread(m,"泡泡");
  13. /*
  14. m.run()不可用,不是多线程的启动方式
  15. m.start(),报错,Runnable与实现类都没有start方法
  16. */
  17. thread.start();
  18. thread1.start();
  19. thread2.start();
  20. thread3.start();
  21. }
  22. }
  23. //implements Runnable实现多线程
  24. class MyRunnable implements Runnable{
  25. //将具体业务写入run(),实现Runnable接口中的run()
  26. @Override
  27. public void run() {
  28. /*
  29. 获取当前正在执行的线程对象,静态方法可以直接使用Thread调用
  30. Thread.currentThread()
  31. 获取当前正在执行的线程对象,静态方法可以被Thread类名直接调用
  32. 线程对象, getName()获取当前正在执行的线程对象的名称
  33. */
  34. for (int i = 0;i < 10;i++){
  35. System.out.println("第" + i + "次" + Thread.currentThread().getName());
  36. }
  37. }
  38. }

两种方式比较

继承Thread类
优点: 编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this即可获得当前线程
缺点: 自定义的线程类已继承了Thread类,所以后续无法再继承其他的类
实现Runnable接口
优点: 自定义的线程类只是实现了Runnable接口或Callable接口,后续还可以继承其他类,在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码、还有数据分开(解耦),形成清晰的模型,较好地体现了面向对象的思想
缺点: 编程稍微复杂,如想访问当前线程,则需使用Thread.currentThread()方法

售票案例

继承Thread

  1. package cn.tedu.thread;
  2. /**
  3. * 多线程实现方式
  4. * 线程随机性:多个线程对象执行效果不可控,由CPU调度处理,结果具有随机性
  5. * 哪个时间片执行哪个线程以及时间片有多长,只能由CPU控制,无法人为调控
  6. */
  7. public class TestThread {
  8. public static void main(String[] args) {
  9. MyThread m = new MyThread();//对应线程为新建状态
  10. MyThread m1 = new MyThread();//对应线程为新建状态
  11. MyThread m2 = new MyThread();//对应线程为新建状态
  12. MyThread m3 = new MyThread();//对应线程为新建状态
  13. MyThread m4 = new MyThread("马钊");//对应线程为新建状态
  14. /*
  15. 使用run()方法,普通方法的调用
  16. 没有多线程的效果
  17. m.run();
  18. m1.run();
  19. */
  20. m.start();//start()方法会把线程加入就绪队列,以多线程的方式调用
  21. m1.start();
  22. m2.start();
  23. m3.start();
  24. m4.start();
  25. }
  26. }
  27. class MyThread extends Thread{
  28. /*
  29. 线程中的业务必须写在run()里,不执行父类的run(),执行自己的业务
  30. */
  31. @Override
  32. public void run() {
  33. /*
  34. 打印10次当前正在执行的线程名称
  35. getName()可以获取当前正在执行的线程名称
  36. 由父类继承而来,可以直接调用
  37. */
  38. for (int i = 0;i < 10;i++){
  39. System.out.println("第" + (i+1) + "个" + getName());
  40. }
  41. }
  42. public MyThread() {
  43. }
  44. public MyThread(String name) {
  45. /*
  46. 子类对象构造方法触发父类构造方法来给线程起名
  47. */
  48. super(name);
  49. }
  50. }

实现Runnable接口

  1. package cn.tedu.thread;
  2. import java.util.concurrent.ExecutorService;
  3. import java.util.concurrent.Executors;
  4. /**
  5. * 多线程实现方式
  6. */
  7. public class TestRunnable {
  8. public static void main(String[] args) {
  9. MyRunnable target = new MyRunnable();
  10. /*//将接口实现类对象与Thread建立关系
  11. Thread thread = new Thread(target"马钊");
  12. Thread thread1 = new Thread(target,"桂宏宇");
  13. Thread thread2 = new Thread(target,"雨来");
  14. Thread thread3 = new Thread(target,"泡泡");
  15. *//*
  16. m.run()不可用,不是多线程的启动方式
  17. m.start(),报错,Runnable与实现类都没有start方法
  18. *//*
  19. thread.start();
  20. thread1.start();
  21. thread2.start();
  22. thread3.start();*/
  23. /*
  24. 创建线程池
  25. Executors
  26. newFixedThreadPool(线程数)方法创建指定线程数的线程池
  27. 线程池类型为:ExecutorService
  28. */
  29. ExecutorService pool = Executors.newFixedThreadPool(5);
  30. for (int i = 0;i < 5;i++){
  31. //使用池对象完成任务,人任务参数为target
  32. pool.execute(target);
  33. }
  34. }
  35. }
  36. //implements Runnable实现多线程
  37. class MyRunnable implements Runnable{
  38. //将具体业务写入run(),实现Runnable接口中的run()
  39. @Override
  40. public void run() {
  41. /*
  42. 获取当前正在执行的线程对象,静态方法可以直接使用Thread调用
  43. Thread.currentThread()
  44. 获取当前正在执行的线程对象,静态方法可以被Thread类名直接调用
  45. 线程对象, getName()获取当前正在执行的线程对象的名称
  46. */
  47. for (int i = 0;i < 10;i++){
  48. System.out.println("第" + i + "次" + Thread.currentThread().getName());
  49. }
  50. }
  51. }

判断程序有无线程安全问题

在多线程数据中+有共享数据+多条语句操作共享数据